BoundaryML/baml · error
baml.fetch_as: expected header value to be a string, got {}
Error message
baml.fetch_as: expected header value to be a string, got {} What it means
Runtime type-validation error inside the baml.fetch_as host function: the `headers` field of the request argument carried a value that is not a string (e.g. an int or nested map) when building the HTTP HeaderMap. The per-header type check fires during request construction, before any network I/O.
Source
Thrown at engine/baml-runtime/src/async_vm_runtime.rs:643
_ => break 'res Err(anyhow!(
"baml.fetch_as: expected method to be a valid HTTP method, got {}",
method
))
};
if let Some(BamlValue::Map(headers)) = fields.get("headers") {
let mut header_map = reqwest::header::HeaderMap::new();
for (k, v) in headers {
let Ok(key) = reqwest::header::HeaderName::from_str(k) else {
break 'res Err(anyhow!(
"baml.fetch_as: expected header key to be a valid HTTP header name, got {}",
k
));
};
let Some(value_as_string) = v.as_str() else {
break 'res Err(anyhow!(
"baml.fetch_as: expected header value to be a string, got {}",
v
));
};
let Ok(value) = reqwest::header::HeaderValue::from_str(value_as_string) else {
break 'res Err(anyhow!(
"baml.fetch_as: expected header value to be a string, got {}",
v
));
};
header_map.insert(key, value);
}
req = req.headers(header_map);
}
View on GitHub (pinned to bd85ce9dee)
Solutions
- Convert header values to strings before putting them in the headers map (e.g. 42 → "42")
- Ensure variables used as header values are non-null strings at runtime
- Restructure headers so only string values are included
Example fix
// before (BAML)
let req = baml.HttpRequest{url: "https://api.example.com", method: "Get", headers: {"Content-Length": 42}}
// after
let req = baml.HttpRequest{url: "https://api.example.com", method: "Get", headers: {"Content-Length": "42"}} Defensive patterns
Strategy: type-guard
Validate before calling
function validateHeaderValues(headers) {
for (const [k, v] of Object.entries(headers || {})) {
if (typeof v !== 'string') throw new Error(`Header "${k}" must be a string, got ${typeof v}: ${JSON.stringify(v)}`);
}
} Type guard
function isStringHeaderMap(h) {
return h !== null && typeof h === 'object' && Object.values(h).every(v => typeof v === 'string');
} Try / catch
try {
const data = await runtime.callFunction(fnName, args);
} catch (e) {
if (String(e).includes('expected header value to be a string')) {
// coerce numeric/boolean header values with String(v) and retry
} else throw e;
} Prevention
- Stringify numeric/boolean header values (Content-Length, retries) before the call
- Guard template variables used as header values against null/undefined
- Keep headers typed as Record<string,string> end-to-end
When it happens
Trigger: Passing a header value that is not a string, e.g. headers{"Content-Length": 42} or a null value from an unresolved variable.
Common situations: Numeric or boolean header values (content-length, retries), headers built from JSON where numbers stay numbers, or template variables that resolve to null.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- baml.fetch_as: expected url to be a string, got {}
- baml.fetch_as: expected header key to be a valid HTTP header
- type `{ty}` can't be passed through auto-CLI; deliver it via
- baml.json.deserialize failed: {e:?}
- baml.json.serialize returned non-string value: {other:?}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/8be287fd4f80040d.
Report an issue: GitHub.