xai-org/grok-build · error · ToolError

Failed to build request: {e}

Error message

Failed to build request: {e}

What it means

After the web search tool is built, the client assembles the full responses-API request (model, input, tools, temperature, top_p, max_output_tokens) via CreateResponseArgs and calls .build(). If the builder rejects the assembled arguments it maps the error to "Failed to build request: {e}". This is a client-side request construction failure; no request ever reaches the network.

Source

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

            xai_tool_runtime::ToolError::execution(
                xai_tool_protocol::ToolId::new("web_search").expect("valid"),
                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;

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read the wrapped {e} message to identify which builder field was rejected.
  2. Verify self.model is a valid, non-empty model identifier supported by the responses API.
  3. Check the rs crate changelog for CreateResponseArgs changes and update the construction accordingly.
  4. Build a minimal request (model + input + tool only) to isolate which optional field triggers the failure.

Example fix

// before
let client = WebSearchClient::new("")?; // empty model
// after
let client = WebSearchClient::new("gpt-4o")?; // valid model id
Defensive patterns

Strategy: validation

Validate before calling

function validateSearchRequestArgs(model) {
  if (typeof model !== 'string' || model.trim().length === 0) {
    throw new Error('search requires a non-empty model identifier');
  }
  return true;
}

Try / catch

try {
  result = await client.search(query);
} catch (e) {
  if (String(e).startsWith('Failed to build request')) {
    // surface the wrapped builder message to config owners
    throw new Error('Web search request config invalid: ' + e.message);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling search/search_with_titles with a model the builder deems invalid, or argument combinations the rs crate's CreateResponseArgs rejects (e.g. invalid max_output_tokens, mutually inconsistent fields, or a crate version where a previously optional field is now required).

Common situations: Configured model name is empty or unsupported after an API update; upgrading the rs crate changed builder invariants; programmatic callers overriding model with a bad value from config/env.

Related errors


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