xai-org/grok-build · error · ToolError

Failed to serialize request: {e}

Error message

Failed to serialize request: {e}

What it means

Once built, the request struct is serialized to a serde_json::Value so excluded_domains can be patched into the tools array before sending. If serde_json::to_value(&request) fails, the client returns "Failed to serialize request: {e}". This indicates a serde mismatch between the request type and its Serialize impl (non-string map keys, unserializable field types), not a transport problem.

Source

Thrown at crates/codegen/xai-grok-tools/src/implementations/web_search/client.rs:167

                msg,
            )
        };
        let web_search = rs::WebSearchToolArgs::default()
            .filters(rs::WebSearchToolFilters { allowed_domains })
            .build()
            .map_err(|e| err(format!("Failed to build web search tool: {e}")))?;
        let request = rs::CreateResponseArgs::default()
            .model(self.model.clone())
            .input(query.to_string())
            .tools(vec![rs::Tool::WebSearch(web_search)])
            .store(false)
            .temperature(0.1)
            .top_p(0.95)
            .max_output_tokens(8192u32)
            .build()
            .map_err(|e| err(format!("Failed to build request: {e}")))?;
        let mut body = serde_json::to_value(&request)
            .map_err(|e| err(format!("Failed to serialize request: {e}")))?;
        if let Some(excluded) = excluded_domains.filter(|d| !d.is_empty()) {
            let tool = body
                .get_mut("tools")
                .and_then(|t| t.as_array_mut())
                .and_then(|arr| arr.first_mut())
                .and_then(|t| t.as_object_mut());
            if let Some(tool) = tool {
                let filters = tool
                    .entry("filters")
                    .or_insert_with(|| serde_json::json!({}));
                filters["excluded_domains"] = serde_json::json!(excluded);
            }
        }
        Ok(body)
    }
    /// Wire a 401-attribution callback into this client. Idempotent;
    /// safe to call before or after the first request.
    pub fn with_attribution_callback(

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read the wrapped {e} message; serde names the exact field/path that failed.
  2. Align the rs crate (and serde/serde_json) versions so the request types' Serialize impls match.
  3. Check for custom types added to the request that lack Serialize impls or use non-string map keys.
  4. Reproduce with a minimal CreateResponseArgs + WebSearchTool to see if serialization fails without excluded_domains handling, isolating the offending field.

Example fix

// before — mixed crate versions
rs = { version = "0.8" }  # serde_json = "1.0.90" (old)
// after — aligned versions
rs = { version = "0.9" }
serde_json = "1.0.128"
Defensive patterns

Strategy: try-catch

Try / catch

try {
  result = await client.search(query);
} catch (e) {
  if (String(e).startsWith('Failed to serialize request')) {
    throw new Error('serde failed to serialize web search request: ' + e.message +
      ' — check rs/serde_json version alignment');
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling search/search_with_titles when the request struct (or nested tool/filter types from the rs crate) fails serialization — e.g. a crate version whose types use non-string map keys or contain #[serde(skip)]-incompatible custom types.

Common situations: Mixed/incompatible versions of the rs serialization crate after an upgrade; a custom wrapper type inserted into the request without a Serialize impl; exotic field types in a patched CreateResponseArgs.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/9f37f16d54efbfeb. Report an issue: GitHub.