GitoxideLabs/gitoxide · error

Received HTTP status

Error message

Received HTTP status {}

What it means

The HTTP remote (reqwest-based) received a non-success HTTP status from the server and converts it into an `io::Error` whose message carries the status string, with an ErrorKind mapped by status class (PermissionDenied for 401/403-style auth failures, ConnectionAborted for 5xx, Other otherwise). This indicates the server rejected the request.

Solutions

  1. Check the HTTP status in the message and fix auth: update tokens/credentials via the configured credential helper
  2. Verify the repository URL exists and the account has access
  3. Retry on 5xx/429 with backoff; check the provider's status page for outages

Example fix

// before (403)
let mut conn = gix::connect(url, gix::protocol::transport::Protocol::Http)?;
// after: embed credentials
let url = format!("https://token@github.com/user/repo.git");
let mut conn = gix::connect(url, gix::protocol::transport::Protocol::Http)?;
Defensive patterns

Strategy: retry

Validate before calling

fn check_url(url: &str) -> Result<(), String> {
    let u = gix::url::parse(url.into())?;
    if u.scheme != gix::url::Scheme::Https { return Err("expected https".into()); }
    Ok(())
}

Try / catch

match remote.fetch(...) {
    Err(e) if e.to_string().contains("Received HTTP status") => {
        if e.to_string().contains("403") || e.to_string().contains("401") {
            // refresh credentials via credential helper, then retry once
        } else {
            // 5xx/429: retry with exponential backoff
        }
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any git HTTP operation (fetch/clone/push over the reqwest transport) where the server responds with an error status — 401/403 auth failures, 404 repo not found, 5xx server errors, rate limiting (429).

Common situations: Wrong or expired credentials/tokens; private repository with insufficient access; GitHub/GitLab outages or rate limits; proxy interference returning error pages.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/08decacf3f8034ed. Report an issue: GitHub.

Appendix: source

Thrown at gix-transport/src/client/blocking_io/http/reqwest/remote.rs:197

                    Err(err) => {
                        // `error_for_status()` preserves the final URL for HTTP error responses. Capture it here so
                        // authentication retries after redirected 401 responses use the redirected base URL.
                        if let Some(actual_url) = err.url().map(reqwest::Url::as_str)
                            && actual_url != effective_url
                        {
                            let new_base_url = redirect::base_url(actual_url, &base_url, url.clone())?;
                            *redirected_base_url_shared.lock() = Some(new_base_url);
                        }
                        let err = match err.status() {
                            Some(status) => {
                                let kind = if status == reqwest::StatusCode::UNAUTHORIZED {
                                    std::io::ErrorKind::PermissionDenied
                                } else if status.is_server_error() {
                                    std::io::ErrorKind::ConnectionAborted
                                } else {
                                    std::io::ErrorKind::Other
                                };
                                std::io::Error::new(kind, format!("Received HTTP status {}", status.as_str()))
                            }
                            // Preserve the `reqwest::Error` as the source so the underlying cause -- e.g. a
                            // connection or TLS failure -- isn't lost. It was previously stringified, which
                            // dead-ended `source()` and hid the real reason a request failed. See #2140.
                            None => std::io::Error::other(err),
                        };
                        headers_tx.channel.send(Err(err)).ok();
                        continue;
                    }
                };

                let actual_url = res.url().as_str();
                if actual_url != effective_url.as_str() {
                    let new_base_url = redirect::base_url(actual_url, &base_url, url)?;
                    *redirected_base_url_shared.lock() = Some(new_base_url);
                }

                let send_headers = {

View on GitHub (pinned to e73179060b)