xai-org/grok-build · error · ToolError
Failed to build web search tool: {e}
Error message
Failed to build web search tool: {e} What it means
The web search client constructs an rs::WebSearchToolArgs with allowed_domains filters and calls .build(); when the underlying builder rejects the arguments it maps the error to "Failed to build web search tool: {e}". This happens before any network I/O, so the cause is invalid tool configuration — typically malformed or invalid domain entries in allowed_domains.
Source
Thrown at crates/codegen/xai-grok-tools/src/implementations/web_search/client.rs:155
/// `excluded_domains` is injected into the tool's `filters` after
/// serialization (the backend Responses API accepts it). The request always
/// carries exactly one tool (`web_search`) at index 0.
fn build_request_json(
&self,
query: &str,
allowed_domains: Option<Vec<String>>,
excluded_domains: Option<Vec<String>>,
) -> Result<serde_json::Value, xai_tool_runtime::ToolError> {
let err = |msg: String| {
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());View on GitHub (pinned to bc7f02eddd)
Solutions
- Inspect the wrapped {e} message; it names the exact builder validation that failed.
- Normalize allowed_domains to bare hostnames ("example.com"), stripping scheme/path/wildcards.
- Check the installed rs crate version's WebSearchToolArgs/WebSearchToolFilters docs for changed required fields.
- Test with allowed_domains = None/empty to confirm the tool builds without filters, then re-add entries one at a time.
Example fix
// before
filters: WebSearchToolFilters { allowed_domains: Some(vec!["https://docs.example.com"] ) }
// after
filters: WebSearchToolFilters { allowed_domains: Some(vec!["docs.example.com"]) } Defensive patterns
Strategy: validation
Validate before calling
function normalizeDomains(domains) {
if (!domains) return undefined;
const bare = domains.map(d => String(d)
.replace(/^https?:\/\//, '')
.replace(/\/.*$/, '')
.replace(/^\*\./, ''));
return bare.every(d => /^[a-z0-9.-]+\.[a-z]{2,}$/i.test(d)) ? bare : undefined;
} Type guard
function isValidDomainList(d: unknown): d is string[] {
return Array.isArray(d) && d.length > 0 &&
d.every(x => typeof x === 'string' && /^[a-z0-9.-]+\.[a-z]{2,}$/i.test(x));
} Try / catch
try {
result = await client.search(query, allowedDomains);
} catch (e) {
if (String(e).startsWith('Failed to build web search tool')) {
result = await client.search(query, undefined); // retry without filters
} else { throw e; }
} Prevention
- Always pass bare hostnames (no scheme, path, or wildcard) in allowed_domains.
- Validate domain lists at config-load time, not at request time.
- Pin and review the rs crate version; re-run build tests after upgrades.
When it happens
Trigger: Calling search or search_with_titles with allowed_domains containing entries the builder rejects (invalid hostnames, protocols like "https://" in a domain, empty strings that pass the filter, or builder-required fields missing due to a version change in the rs crate).
Common situations: Passing full URLs instead of bare domains; upgrading the responses/web-search crate where WebSearchToolArgs validation became stricter; config files supplying wildcard or malformed domain patterns.
Related errors
- Failed to build request: {e}
- invalid worktree id from dest: {worktree_id}
- invalid worktree id {:?}
- invalid {GROK_CHAT_LOCAL_WORKSPACE_MODE_ENV}={other:?}; expe
- Error: --session-id must be a valid UUID (got '{session_id}'
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/6036e1f738e621a9.
Report an issue: GitHub.