clockworklabs/SpacetimeDB · critical
Invalid header name from host
Error message
Invalid header name from host
What it means
On the host-to-guest HTTP bridge, header names arriving from the host are converted with http::HeaderName::from_bytes, which accepts only HTTP token characters. A name containing spaces, control bytes, or non-ASCII characters, or an empty name, is rejected - and this .expect converts that rejection into a panic that aborts the call.
Source
Thrown at crates/bindings/src/http.rs:653
let request = http::Request::builder()
.method(method)
.uri(http::Uri::from_str(&uri).expect("Invalid URI from host"))
.body(Body::from_bytes(body))
.expect("Failed to build request");
let (mut parts, body) = request.into_parts();
parts.version = match version {
st_http::Version::Http09 => http::Version::HTTP_09,
st_http::Version::Http10 => http::Version::HTTP_10,
st_http::Version::Http11 => http::Version::HTTP_11,
st_http::Version::Http2 => http::Version::HTTP_2,
st_http::Version::Http3 => http::Version::HTTP_3,
};
parts.headers = headers
.into_iter()
.map(|(k, v)| {
let name = http::HeaderName::from_bytes(k.as_bytes()).expect("Invalid header name from host");
let value = http::HeaderValue::from_bytes(v.as_ref()).expect("Invalid header value from host");
(name, value)
})
.collect();
http::Request::from_parts(parts, body)
}
#[cfg(feature = "unstable")]
pub(crate) fn response_into_wire(response: http::Response<Body>) -> (st_http::Response, Bytes) {
let (parts, body) = response.into_parts();
let st_response = st_http::Response {
headers: parts
.headers
.into_iter()
.map(|(k, v)| (k.map(|k| k.as_str().into()), v.as_bytes().into()))
.collect(),
version: match parts.version {View on GitHub (pinned to 524b4487d9)
Solutions
- Sanitize header names to ASCII token characters before they reach the bridge; drop invalid ones with a logged warning.
- Validate and reject malformed headers (HTTP 400) at your API boundary instead of letting them reach the module.
- Upgrade bindings - newer versions may return an error instead of panicking.
Example fix
// before: panics on malformed input
let name = http::HeaderName::from_bytes(k.as_bytes()).expect("Invalid header name from host");
// after: validate and skip
let Ok(name) = http::HeaderName::from_bytes(k.as_bytes()) else {
log::warn!("dropping invalid header name: {k:?}");
continue;
}; Defensive patterns
Strategy: type-guard
Validate before calling
// Reject malformed header names at your boundary before they are forwarded:
fn header_name_ok(name: &str) -> bool {
!name.is_empty()
&& name.bytes().all(|b| matches!(b, 33..=126) && !b"()<>@,;:\\"/[]?={}".contains(&b))
&& b' ' != 32 // spaces excluded by the 33..=126 range above
} Type guard
fn valid_header_name(name: &str) -> bool {
!name.is_empty() && name.bytes().all(|b| b > 32 && b < 127 && !b"()<>@,;:\\"/[]?={}".contains(&b))
} Try / catch
// It panics, not returns Err: catch it at the embedding boundary.
let outcome = std::panic::catch_unwind(|| handle_request(request));
if outcome.is_err() {
// Trap already unwound this call: log the offending headers, respond 400/500,
// and keep the process alive.
} Prevention
- Never build header names from unvalidated user or gateway input.
- Restrict names to ASCII token characters; reject non-ASCII and control bytes early.
- Fuzz your HTTP boundary so malformed names are rejected before reaching the bridge.
When it happens
Trigger: The host supplies a request whose header name is malformed: a space instead of a hyphen ("Content Type"), non-ASCII/UTF-8 names, control characters, or the empty string - typically unvalidated client or gateway input forwarded into the module's HTTP API.
Common situations: Proxies/gateways injecting malformed headers; header names built from unvalidated user input; fuzzed HTTP requests reaching the bridge.
Related errors
- should have a pending mutable anon tx as `procedure_start_mu
- holding `&mut HandlerContext`, so should not be in a tx alre
- transaction retry failed again
- Failed to parse JWT payload
- Missing 'sub' claim
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/1fd6ddee367ea75d.
Report an issue: GitHub.