seanmonstar/reqwest · error · reqwest::Error
HTTP status client error ({code})
Error message
HTTP status client error ({code}) What it means
A `Kind::Status(4xx, ...)` error (error.rs:280-296). It is produced only by `Response::error_for_status()` (response.rs:382, 413) when the server returned a 4xx client-error status. reqwest does NOT throw this automatically — `.send()` returns the `Response` regardless of status; the error only appears if the caller explicitly turns a 4xx into an error.
Source
Thrown at src/error.rs:373
pub(crate) fn request<E: Into<BoxError>>(e: E) -> Error {
Error::new(Kind::Request, Some(e))
}
pub(crate) fn dns<E: Into<BoxError>>(e: E) -> BoxError {
Box::new(DnsError { inner: e.into() })
}
pub(crate) fn redirect<E: Into<BoxError>>(e: E, url: Url) -> Error {
Error::new(Kind::Redirect, Some(e)).with_url(url)
}
pub(crate) fn status_code(
url: Url,
status: StatusCode,
#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] reason: Option<hyper::ext::ReasonPhrase>,
) -> Error {
Error::new(
Kind::Status(
status,
#[cfg(not(all(
target_arch = "wasm32",
any(target_os = "unknown", target_os = "none")
)))]
reason,
),
None::<Error>,
)
.with_url(url)
}
pub(crate) fn url_bad_scheme(url: Url) -> Error {
Error::new(Kind::Builder, Some(BadScheme)).with_url(url)
}
pub(crate) fn url_invalid_uri(url: Url) -> Error {View on GitHub (pinned to 17e9bcb51c)
Solutions
- Don't call `error_for_status()` blindly; inspect `resp.status()` and the body first so you can read the API's error message.
- Handle 4xx semantically (e.g. 401 → refresh token, 429 → back off via `Retry-After`).
- Use `error_for_status_ref()` when you still need the response body on failure.
Example fix
// before
let body: T = resp.error_for_status()?.json().await?; // loses API error detail
// after
let status = resp.status();
if status.is_client_error() {
let text = resp.text().await.unwrap_or_default();
match status.as_u16() {
401 => return Err(AuthError::Expired.into()),
429 => return Err(RateLimited.into()),
_ => anyhow::bail!("api {status}: {text}"),
}
}
let body: T = resp.json().await?; Defensive patterns
Strategy: validation
Validate before calling
// Inspect status before converting to an error.
let status = resp.status();
if status.is_client_error() {
let body = resp.text().await.unwrap_or_default();
return Err(handle_client_error(status, body));
}
Type guard
fn is_client_status(e: &reqwest::Error) -> bool {
e.status().map(|c| c.is_client_error()).unwrap_or(false)
}
Try / catch
let resp = client.get(&url).send().await?;
if resp.status().is_client_error() {
let text = resp.text().await.unwrap_or_default();
return Err(ApiError::from_status(resp.status(), text));
}
let body = resp.json::<T>().await?; Prevention
- Don't call error_for_status() blindly; read status and body first.
- Map 4xx codes to domain errors (401→refresh, 429→backoff) instead of generic failures.
- Use error_for_status_ref() if you still need the body on the failure path.
When it happens
Trigger: Calling `resp.error_for_status()?` (or `error_for_status_ref()` returning Err) after a 400/401/403/404/409/422/429 response.
Common situations: Bad request payload (400), missing/expired auth token (401/403), unknown resource (404), conflict (409), validation failure (422), rate limited (429).
Related errors
AI-assisted analysis of seanmonstar/reqwest@17e9bcb51c (2026-08-06).
Data as JSON: /data/errors/15f4135240f712d6.json.
Report an issue: GitHub.