BoundaryML/baml · error · ApiError

Failed to deserialize response: {0}

Error message

Failed to deserialize response: {0}

What it means

This is the Display message of ApiError::Deserialize in the BAML trace publisher. It is thrown when the response from the BAML tracing API could not be deserialized with serde_json — the request succeeded at the HTTP level but the body is not the JSON shape the client expects. Usually indicates an API/client version mismatch or a proxy/error page returning non-JSON.

Source

Thrown at engine/baml-runtime/src/tracingv2/publisher/publisher.rs:199

        };

        match timeout(timeout_duration, fut).await {
            Ok(res) => res,
            Err(_) => Err(ApiError::Timeout(timeout_duration)),
        }
    }
}

#[derive(thiserror::Error, Debug)]
pub enum ApiError {
    #[error("Transport error: {0}")]
    Transport(reqwest::Error),
    #[error("HTTP error: {status} {body}")]
    Http {
        status: reqwest::StatusCode,
        body: String,
    },
    #[error("Failed to deserialize response: {0}")]
    Deserialize(serde_json::Error),
    #[error("Request timed out after {0:?}")]
    Timeout(Duration),
}

impl TypeLookup for RuntimeAST {
    fn type_lookup(&self, name: &str) -> Option<Arc<baml_rpc::BamlTypeId>> {
        self.ast.type_lookup(name)
    }

    fn function_lookup(&self, name: &str) -> Option<Arc<baml_rpc::ast::tops::BamlFunctionId>> {
        self.ast.function_lookup(name)
    }

    fn baml_src_hash(&self) -> Option<String> {
        self.ast.baml_src_hash()
    }
}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Log the raw response body to see what was actually returned.
  2. Upgrade the BAML runtime and the BAML cloud/gateway to matching versions.
  3. Bypass or fix any proxy that rewrites responses (check for HTML error pages).
  4. Report schema mismatch to BAML if versions are current.

Example fix

// before
let resp: CheckResponse = serde_json::from_str(&body)?;
// after
let resp: CheckResponse = serde_json::from_str(&body)
    .map_err(|e| { log::error!("unexpected collector body: {body}"); ApiError::Deserialize(e) })?;
Defensive patterns

Strategy: try-catch

Validate before calling

// verify server/client compatibility
body = requests.get(f"{BAML_URL}/v1/check", headers=auth_headers).text
json.loads(body)  # must not raise; HTML responses indicate a proxy issue

Type guard

fn is_deserialize_err(e: &ApiError) -> bool { matches!(e, ApiError::Deserialize(_)) }

Try / catch

match res {
    Err(ApiError::Deserialize(e)) => {
        log::error!("collector sent non-JSON/unexpected body: {e}");
        // fall back: disable export or queue events for later
    }
    Err(e) => log::error!("publish failed: {e}"),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: The collector responds with JSON that does not match the expected response struct (e.g. a check_response for source upload missing fields or changed schema), or a proxy/gateway returns an HTML error page, or an incompatible BAML server version is deployed.

Common situations: Self-hosted/older BAML gateway version returning an older response schema, a corporate proxy intercepting with an HTML auth page, or a new BAML client talking to an old backend.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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