headroomlabs-ai/headroom · error · ProxyError::InvalidUpstream

invalid upstream URL: {0}

Error message

invalid upstream URL: {0}

What it means

The configured upstream base URL could not be turned into a valid request target. Unlike ProxyError::Upstream (transport-time), this fails while building the outbound request from configuration, and maps to 502 Bad Gateway since the fault is on the proxy's chosen upstream, not the client's request.

Source

Thrown at crates/headroom-proxy/src/error.rs:12

//! Error types for the proxy.

use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum ProxyError {
    #[error("upstream request failed: {0}")]
    Upstream(#[from] reqwest::Error),

    #[error("invalid upstream URL: {0}")]
    InvalidUpstream(String),

    #[error("invalid header: {0}")]
    InvalidHeader(String),

    #[error("websocket error: {0}")]
    WebSocket(String),

    #[error("io error: {0}")]
    Io(#[from] std::io::Error),

    /// PR-A8 / P5-59: request body exceeded the configured cap. RFC 7231
    /// §6.5.11: 413 Payload Too Large. Previously surfaced as
    /// `InvalidHeader` (400) which mis-classified an oversize body as a
    /// header parse error; clients with retry-on-413 logic broke.
    #[error("request body exceeds configured limit: {0}")]
    PayloadTooLarge(String),

View on GitHub (pinned to 322425c43b)

Solutions

  1. Fix the configured URL to include a full valid scheme+host, e.g. https://api.example.com — the {0} payload names what was invalid.
  2. Add startup validation that parses the upstream URL once in main so misconfiguration is fatal at boot, not per-request 502s.
  3. Echo the effective config at startup (log the resolved upstream URL) to catch env-var sourcing mistakes.

Example fix

# before
export UPSTREAM_URL=bedrock-runtime.us-east-1.amazonaws.com

# after
export UPSTREAM_URL=https://bedrock-runtime.us-east-1.amazonaws.com
Defensive patterns

Strategy: validation

Validate before calling

// Startup: fail fast on unparseable upstream URL
let base = url::Url::parse(&cfg.upstream)
    .map_err(|e| format!("invalid upstream URL {cfg_upstream:?}: {e}"))?;

Prevention

When it happens

Trigger: Config value like 'bedrock-runtime.us-east-1.amazonaws.com' (no scheme), 'http://[bad', or an upstream override whose path/query portion is invalid; URL parsed at request-build time and rejected.

Common situations: Typo'd or scheme-less --upstream/--base-url flag; environment variable with trailing whitespace or a newline; swapping an OpenAI-style URL into a Bedrock-style config (or vice versa) with incompatible path expectations.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/793d6049ef4e6b06. Report an issue: GitHub.