zeroclaw-labs/zeroclaw · error
Host '{host}' is not in browser.allowed_domains
Error message
Host '{host}' is not in browser.allowed_domains What it means
For public (non-private) hosts, browser_open checks the URL host against [browser].allowed_domains, which supports exact and wildcard patterns after normalization. A host matching no pattern is rejected — the allowlist is the only route in for public hosts.
Source
Thrown at crates/zeroclaw-tools/src/browser_open.rs:78
"Browser tool is enabled but no allowed_domains are configured. Add [browser].allowed_domains in config.toml"
);
}
let host = extract_host(url)?;
let private_host = domain_guard::is_private_or_local_host(&host);
let private_host_allowed = private_host
&& domain_guard::host_matches_allowlist(&host, &self.allowed_private_hosts);
if private_host && !private_host_allowed {
anyhow::bail!("Blocked local/private host: {host}");
}
if private_host_allowed {
return Ok(url.to_string());
}
if !domain_guard::host_matches_allowlist(&host, &self.allowed_domains) {
anyhow::bail!("Host '{host}' is not in browser.allowed_domains");
}
Ok(url.to_string())
}
}
#[async_trait]
impl Tool for BrowserOpenTool {
fn name(&self) -> &str {
"browser_open"
}
fn description(&self) -> &str {
"Open an approved HTTP/HTTPS URL in the system browser. Security constraints: allowlist-only domains, no local/private hosts, no scraping."
}
fn parameters_schema(&self) -> serde_json::Value {
json!({View on GitHub (pinned to 88bb9c8533)
Solutions
- Add the host (exact or wildcard) to [browser].allowed_domains in config
- Check whether the pattern covers subdomains — docs.example.com needs an entry for example.com or a *.example.com pattern
- Keep the allowlist aligned with the sites the agent is actually authorized to open
Example fix
# config.toml — before [browser] allowed_domains = ["example.com"] # after [browser] allowed_domains = ["example.com", "sso.provider.com"]
Defensive patterns
Strategy: validation
Validate before calling
let host_matches = |h: &str, p: &str| {
let (h, p) = (h.to_lowercase(), p.to_lowercase());
h == p || h.ends_with(&format!(".{p}")) || wildcard_covers(&h, &p)
};
if !allowed_domains.iter().any(|p| host_matches(&host, p)) {
return Err(format!("host '{host}' is not in browser.allowed_domains"));
} Try / catch
match open_tool.execute(args).await {
Ok(res) if res.success => { /* ... */ }
Ok(res) => {
if res.error.as_deref().unwrap_or_default().contains("not in browser.allowed_domains") {
// add the host (exact or wildcard) to config, or drop the URL
}
}
Err(e) => return Err(e),
} Prevention
- Keep allowed_domains aligned with sites the agent is authorized to open
- Add auth/CDN domains (e.g. sso.provider.com) discovered during login flows
- Review the allowlist whenever onboarding new integrations
When it happens
Trigger: url="https://other.com" while allowed_domains=["example.com"]; subdomains pass only when the configured pattern covers them (exact entry or a wildcard such as *.example.com).
Common situations: Agents following links beyond the originally scoped site; new CDN or auth domains (sso.provider.com) appearing during login flows; wildcard patterns not covering a different TLD.
Related errors
- domain '{}' is not in browser_delegate allowed_domains
- domain '{}' is blocked by browser_delegate policy
- Blocked local/private host: {host}
- Workspace path {} is not in runtime.docker.allowed_workspace
- Serial path not allowed: {}. Allowed: {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/739d752dd4c72d8c.
Report an issue: GitHub.