a-b-street/abstreet · error

HTTP error

Error message

HTTP error {}: {}

What it means

abstio::http_post performs an HTTP POST and returns the body as text. If the response status is a client error (4xx) or server error (5xx), it bails with "HTTP error {status}: {body}" so the caller can see both the status code and the server's error message.

Solutions

  1. Inspect the status code and body in the error to identify whether the problem is auth, payload, or server-side.
  2. Verify the URL, headers (Content-Type, auth), and request body format match what the endpoint expects.
  3. Add retry logic with backoff for transient 5xx (e.g. 502/503), but not for 4xx errors.

Example fix

// before
let text = abstio::http_post(url, body).await?;
// after
match abstio::http_post(url, body).await {
    Ok(text) => Ok(text),
    Err(err) => { log::error!("POST to {} failed: {}", url, err); Err(err) }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight checks before POST:
assert!(!auth_token.is_empty(), "missing auth token");
assert!(url.starts_with("https://"), "unexpected URL");

Try / catch

match abstio::http_post(url, body).await {
    Ok(text) => Ok(text),
    Err(e) if e.to_string().contains("HTTP error 5") => retry_with_backoff(url, body).await,
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Any POST whose server responds with 4xx/5xx: bad endpoint URL, missing/invalid authentication, malformed request payload, server-side failure, rate limiting.

Common situations: Hitting an API whose auth token expired; posting to a URL that only accepts GET; network proxies returning 502/503; server validation rejecting the posted JSON.

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 a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/f1db5d875065a592. Report an issue: GitHub.

Appendix: source

Thrown at abstio/src/http.rs:18

use anyhow::{Context, Result};

/// Performs an HTTP POST request and returns the response.
pub async fn http_post<U: AsRef<str>, B: Into<reqwest::Body>>(url: U, body: B) -> Result<String> {
    let url = url.as_ref();
    info!("HTTP POST to {}", url);
    let resp = reqwest::Client::new()
        .post(url)
        .body(body)
        .send()
        .await
        .with_context(|| url.to_string())?;
    let status = resp.status();
    let text = resp.text().await.with_context(|| url.to_string())?;
    // With error_for_status{_ref}, it's unclear how to propagate errors and also get the error
    // message from the body, so do this
    if status.is_client_error() || status.is_server_error() {
        bail!("HTTP error {}: {}", status, text);
    }
    Ok(text)
}

/// Performs an HTTP GET request and returns the raw response. Unlike the variations in
/// download.rs, no progress -- but it works on native and web.
pub async fn http_get<I: AsRef<str>>(url: I) -> Result<Vec<u8>> {
    let url = url.as_ref();
    info!("HTTP GET {}", url);
    let resp = reqwest::get(url).await?.error_for_status()?.bytes().await?;
    Ok(resp.to_vec())
}

View on GitHub (pinned to 0964f29315)