headroomlabs-ai/headroom · error · ProxyError::InvalidUpstream
invalid upstream URL
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.
Solutions
- Fix the configured URL to include a full valid scheme+host, e.g. https://api.example.com — the {0} payload names what was invalid.
- Add startup validation that parses the upstream URL once in main so misconfiguration is fatal at boot, not per-request 502s.
- 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
- Always include scheme (https://) in configured upstream URLs.
- Log the resolved effective config at boot to catch env-var mistakes.
- Validate in main so misconfig aborts startup instead of 502ing per request.
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
- upstream request failed
- bedrock_upstream_exception
- compression engine startup failed
- failed to read simulator config
- metrics response build error
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/793d6049ef4e6b06.
Report an issue: GitHub.
Appendix: 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)