BoundaryML/baml · warning
HTTP body is not JSON
Error message
HTTP body is not JSON: {} What it means
HttpBody::json first requires the body to be UTF-8 (via text()) and then parses it as JSON with serde_json. Either failure surfaces as "HTTP body is not JSON: {e}". It means the recorded HTTP body was not a JSON document.
Solutions
- Check the HTTP status and Content-Type header; only parse JSON when Content-Type is application/json.
- Inspect body.text() first and handle empty/HTML bodies before calling json().
- Handle streaming (SSE) bodies line-by-line rather than as a single JSON value.
- Fall back to raw()/as_serde_value byte-array representation when JSON parsing is not required.
Example fix
// before
let v = body.json()?;
// after
let v = match body.json() {
Ok(v) => v,
Err(e) => { log::warn!("non-JSON body: {}", body.text().unwrap_or("<binary>")); serde_json::Value::Null }
}; Defensive patterns
Strategy: type-guard
Validate before calling
let parseable = std::str::from_utf8(body.raw()).map(|s| serde_json::from_str::<serde_json::Value>(s).is_ok()).unwrap_or(false);
Type guard
fn is_json_body(b: &HttpBody) -> bool { b.text().map(|t| serde_json::from_str::<serde_json::Value>(t).is_ok()).unwrap_or(false) } Try / catch
let v = body.json().unwrap_or(serde_json::Value::Null);
Prevention
- Verify Content-Type is application/json before parsing
- Inspect status codes — error pages are usually HTML
- Treat SSE/streaming bodies separately from single JSON documents
When it happens
Trigger: Calling HttpBody::json() on bodies that are HTML error pages, plain-text messages, empty bodies, or truncated/invalid JSON from an upstream provider.
Common situations: Provider outages returning HTML 502 pages; streaming/SSE payloads inspected as one JSON document; endpoints returning form-encoded or plain-text responses.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Depth limit reached. Likely a circular reference.
- Depth limit reached. Likely a circular reference.
- Failed to parse JSON
- Failed to parse JSON
- Failed to parse JSON response
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/12b310fb48c097bb.
Report an issue: GitHub.
Appendix: source
Thrown at engine/baml-lib/baml-types/src/tracing/events.rs:295
}
}
impl HTTPBody {
pub fn new(body: Vec<u8>) -> Self {
Self { raw: body }
}
pub fn raw(&self) -> &[u8] {
&self.raw
}
pub fn text(&self) -> anyhow::Result<&str> {
std::str::from_utf8(&self.raw).map_err(|e| anyhow::anyhow!("HTTP body is not UTF-8: {}", e))
}
pub fn json(&self) -> anyhow::Result<serde_json::Value> {
serde_json::from_str(self.text()?)
.map_err(|e| anyhow::anyhow!("HTTP body is not JSON: {}", e))
}
/// Returns the HTTP body as a [`serde_json::Value`].
///
/// If the body is not UTF-8 or JSON, it is returned as an array of bytes.
/// Used as input for [`serde_json::to_string_pretty`].
pub fn as_serde_value(&self) -> serde_json::Value {
self.json()
.or_else(|_e| self.text().map(|s| serde_json::Value::String(s.into())))
.unwrap_or_else(|_e| {
serde_json::Value::Array(
self.raw()
.iter()
.map(|byte| serde_json::Value::from(*byte))
.collect(),
)
})
}View on GitHub (pinned to bd85ce9dee)