Skip to content

Improve CLI HTTP error messages#490

Open
mozzieongit wants to merge 14 commits intomainfrom
improve-cli-errors
Open

Improve CLI HTTP error messages#490
mozzieongit wants to merge 14 commits intomainfrom
improve-cli-errors

Conversation

@mozzieongit
Copy link
Member

@mozzieongit mozzieongit commented Mar 2, 2026

Originally, we often just printed the debug output of errors. That is very confusing and noisy. This PR changes that to chaining together the human-readable error descriptions of nested errors (relevant e.g. for tcp connection errors).

Example errors:

2026... ERROR cascade: Error: HTTP response decoding failed: expected value at line 1 column 1
2026... ERROR cascade: Error: HTTP connection failed: client error (Connect): tcp connect error: Connection refused (os error 111)
2026... ERROR cascade: Error: HTTP connection timed out
2026... ERROR cascade: Error: HTTP request failed: client error (SendRequest): connection closed before message completed

2026... ERROR cascade: Error: HTTP request failed with status code 404

Fixes: #481

@mozzieongit mozzieongit requested a review from ximon18 March 2, 2026 15:04
@mozzieongit mozzieongit self-assigned this Mar 2, 2026
@mozzieongit mozzieongit added the enhancement New feature or request label Mar 2, 2026
Copy link
Contributor

@bal-e bal-e left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@mozzieongit
Copy link
Member Author

As #481 explicitly mentions 404 errors, I set out to also make them better visible. As the code got very repetitive, I created new helper functions on the client.

The error output can now contain:

2026... ERROR cascade: Error: HTTP request failed with status code 404

@mozzieongit mozzieongit requested a review from bal-e March 6, 2026 13:39
Copy link
Contributor

@bal-e bal-e left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only does this improve functionality, we also lose a good chunk of boilerplate! I only have some minor comments.

Comment on lines +82 to +83
# The API uses 'serde' for transforming high-level types to and from the
# underlying wire format. The CLI needs to deserialize them.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for documenting the need for the dependency! If it's only needed for cascade-api, perhaps cascade-api should re-export its serde dependency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, instead of CLI's Cargo.toml including serde, the API crate would do pub use serde::DeserializeOwned somewhere and the CLI would do use api_crate::DeserializeOwned?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API crate would expose the whole serde crate, as cascade_api::serde or cascade_api::dep::serde (the latter is a convention also found in domain).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +95 to +97
/// Format HTTP errors with message based on error type, and chain error
/// descriptions together instead of simply printing the Debug representation
/// (which is confusing for users).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the way this method now presents higher-level error types to the user. However, there may be information in the fmt::Display impl of err that we don't include (e.g. in the unknown error kind case). Would it be worthwhile to also log the full error (e.g. via tracing::debug!) so users can provide us more details easily?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean with "full error"?
Display? That is only a very reduces error message without much info.
Debug? I think everything there is included in the source() concatenation.

I don't know what the Display would include that the error.source() doesn't.

Some quick examples:

Display: DEBUG cascade::client: Formatting error: error sending request for url (http://127.0.0.1:4539/zone/add)
Debug: DEBUG cascade::client: Formatting error: reqwest::Error { kind: Request, url: "http://127.0.0.1:4539/zone/add", source: hyper_util::client::legacy::Error(SendRequest, hyper::Error(IncompleteMessage)) }
Concat'd-string: ERROR cascade: HTTP request failed: client error (SendRequest): connection closed before message completed


Display: DEBUG cascade::client: Formatting error: HTTP status client error (404 Not Found) for url (http://127.0.0.1:4539/zone/example.com/status)
Debug: DEBUG cascade::client: Formatting error: reqwest::Error { kind: Status(404, None), url: "http://127.0.0.1:4539/zone/example.com/status" }
Concat'd-string: ERROR cascade: HTTP request failed with status code 404

Display: DEBUG cascade::client: Formatting error: error sending request for url (http://127.0.0.1:4539/zone/example.com/status)
Debug: DEBUG cascade::client: Formatting error: reqwest::Error { kind: Request, url: "http://127.0.0.1:4539/zone/example.com/status", source: hyper_util::client::legacy::Error(Connect, ConnectError("tcp connect error", 127.0.0.1:4539, Os { code: 111, kind: ConnectionRefused, message: "Connection refused" })) }
Concat'd-str: ERROR cascade: HTTP connection failed: client error (Connect): tcp connect error: Connection refused (os error 111)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sharing such detailed examples! I was mostly thinking of the else branch in the code, which you've commented as

Covers unknown errors, non-OK HTTP status codes, errors "related to the request" [1], errors "related to the request or response body" [1], errors "from a type Builder" [1], errors "from a RedirectPolicy." [1], errors "related to a protocol upgrade request" [1]

That sounds like a lot of different possibilities. Are we sure the source() printing loop will allow us to disambiguate between them?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure the source-loop will cover them all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the comment to say that it covers all other errors, as the explicit mention of all those types didn't really add any value.

Also, the Display impl prints less (and less useful) information than the source-loop will give, and the Debug print contains all the information that the source-loop will print, just with all the wrapping type names, but not more from my testing. The source-loop will allow us to disambiguate between the errors just as much as the Debug-print would.

@ximon18 ximon18 added this to the 0.1.0-beta1 milestone Mar 9, 2026
@mozzieongit
Copy link
Member Author

Interesting that CI didn't run for debad66 and c3eb99c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Daemon HTTP 404 errors result in confusing CLI errors.

3 participants