BoundaryML/baml · warning

HTTP body is not UTF-8

Error message

HTTP body is not UTF-8: {}

What it means

HttpBody::text decodes the recorded raw HTTP body bytes as UTF-8. If the bytes are not valid UTF-8 (binary content, compressed bodies, other encodings), the underlying from_utf8 error is wrapped as "HTTP body is not UTF-8: {e}". The library only exposes body text when it is valid UTF-8.

Solutions

  1. Use raw() to get the bytes and decode with the correct encoding (e.g. inflate gzip, convert latin-1).
  2. Check the Content-Encoding/Content-Type headers before calling text().
  3. Call json() only for bodies known to be UTF-8 JSON; otherwise use the byte-array fallback path.

Example fix

// before
let s = body.text()?;
// after
let bytes = body.raw();
let s = String::from_utf8_lossy(bytes); // or gunzip first if Content-Encoding: gzip
Defensive patterns

Strategy: fallback

Validate before calling

let is_utf8 = std::str::from_utf8(body.raw()).is_ok();

Type guard

fn is_text_body(b: &HttpBody) -> bool { std::str::from_utf8(b.raw()).is_ok() }

Try / catch

let text = match body.text() { Ok(t) => t.to_string(), Err(_) => String::from_utf8_lossy(body.raw()).into_owned() };

Prevention

When it happens

Trigger: Calling HttpBody::text() (directly or via json()) on a body containing binary data — e.g. gzip-compressed responses, image bytes, or non-UTF-8 character sets like latin-1.

Common situations: Tracing/event inspection of LLM HTTP responses where a provider returned compressed or binary payloads; proxies returning binary error pages; responses with charset other than UTF-8.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/6aa571d55550b4f0. Report an issue: GitHub.

Appendix: source

Thrown at engine/baml-lib/baml-types/src/tracing/events.rs:290

                Err(_) => format!("[{} bytes]", self.raw.len()),
            }
        };

        f.debug_struct("HTTPBody").field("raw", &preview).finish()
    }
}

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()

View on GitHub (pinned to bd85ce9dee)