tinyhumansai/openhuman · error

Brave video response shape changed: {e}

Error message

Brave video response shape changed: {e}

What it means

Raised inside the Brave video-search tool's execute when deserializing the Brave VideoSearch API response fails because the JSON shape no longer matches the structs the tool models (missing/renamed fields, changed nesting). It is an upstream-contract-drift signal — the faulty input is Brave's response payload, not the user's query — surfaced to the agent transcript as a tool error rather than a panic.

Source

Thrown at src/openhuman/search/tools/brave.rs:600

                "freshness": { "type": "string", "description": "Time filter: 'pd', 'pw', 'pm', 'py'." }
            },
            "required": ["query"]
        })
    }

    async fn execute(&self, args: Value) -> anyhow::Result<ToolResult> {
        let query = extract_query(&args)?;
        let count = clamped_count(&args, self.cfg.max_results, 20);
        let mut q: Vec<(&str, String)> = vec![("q", query.clone()), ("count", count.to_string())];
        if let Some(c) = pub_string(&args, "country") {
            q.push(("country", c));
        }
        if let Some(f) = pub_string(&args, "freshness") {
            q.push(("freshness", f));
        }
        let raw = brave_get(&self.cfg, "/videos/search", &q).await?;
        let parsed: VideoResp = serde_json::from_value(raw)
            .map_err(|e| anyhow::anyhow!("Brave video response shape changed: {e}"))?;
        if parsed.results.is_empty() {
            return Ok(ToolResult::success(format!("No videos found for: {query}")));
        }
        let mut lines = vec![format!("Video results for: {query} (via Brave)")];
        for (i, r) in parsed.results.iter().take(count).enumerate() {
            let title = if r.title.trim().is_empty() {
                "Untitled"
            } else {
                r.title.trim()
            };
            lines.push(format!("{}. {}", i + 1, title));
            lines.push(format!("   {}", r.url.trim()));
            if let Some(meta) = r.video.as_ref() {
                if let Some(creator) = meta.creator.as_deref() {
                    let creator = creator.trim();
                    if !creator.is_empty() {
                        lines.push(format!("   Creator: {creator}"));
                    }

View on GitHub (pinned to 7491200858)

Solutions

  1. Compare the failing payload against current Brave VideoSearch API docs and update the response structs.
  2. Make the deserialization tolerant: default optional fields, ignore unknown fields, or parse defensively field by field.
  3. Add a pinned response fixture test so future Brave API changes fail in CI instead of in the field.
  4. Retry the search with a different tool (e.g. web search) to still serve the user's request.
Defensive patterns

Strategy: fallback

When it happens

Trigger: Thrown at src/openhuman/search/tools/brave.rs:600 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/3683b967fc457766. Report an issue: GitHub.