zeroclaw-labs/zeroclaw · error
domain '{}' is blocked by browser_delegate policy
Error message
domain '{}' is blocked by browser_delegate policy What it means
browser_delegate checks the blocked_domains denylist first, and a match aborts immediately — deny always wins, even when the domain is also allowlisted or the allowlist is empty. This is a hard policy boundary: the subprocess never receives tasks referencing blocked domains.
Source
Thrown at crates/zeroclaw-tools/src/browser_delegate.rs:102
anyhow::Error::msg(format!("invalid URL '{}': {}", url, e))
})?;
// Only allow http/https schemes
let scheme = parsed.scheme();
if scheme != "http" && scheme != "https" {
anyhow::bail!("unsupported URL scheme: {}", scheme);
}
let domain = parsed.host_str().unwrap_or("").to_string();
if domain.is_empty() {
anyhow::bail!("URL has no host: {}", url);
}
// Check blocked domains first (deny takes precedence)
for blocked in &self.config.blocked_domains {
if domain_matches(&domain, blocked) {
anyhow::bail!("domain '{}' is blocked by browser_delegate policy", domain);
}
}
// If allowed_domains is non-empty, it acts as an allowlist
if !self.config.allowed_domains.is_empty() {
let allowed = self
.config
.allowed_domains
.iter()
.any(|d| domain_matches(&domain, d));
if !allowed {
anyhow::bail!(
"domain '{}' is not in browser_delegate allowed_domains",
domain
);
}
}
View on GitHub (pinned to 88bb9c8533)
Solutions
- If access is legitimately required, remove or narrow the entry in browser_delegate blocked_domains configuration
- Otherwise drop the blocked URL from the task
- Remember precedence: adding the domain to allowed_domains will NOT unblock it
Example fix
# config.toml — before [browser_delegate] blocked_domains = ["example.com"] # after (access granted deliberately) blocked_domains = []
Defensive patterns
Strategy: try-catch
Try / catch
match delegate.execute(args).await {
Ok(res) if res.success => { /* ... */ }
Ok(res) => {
if res.error.as_deref().unwrap_or_default().contains("blocked by browser_delegate policy") {
// policy denial: drop the blocked URL; do not retry or try to bypass
}
}
Err(e) => return Err(e),
} Prevention
- Treat blocked_domains denials as final — allowlist entries cannot override them
- Log denials so policy owners can audit and update the denylist deliberately
- Review the denylist when onboarding new legitimate domains
When it happens
Trigger: A task URL (or a URL embedded in task text) whose domain matches a blocked_domains entry. Matching is exact or parent-domain: docs.example.com matches an entry of example.com.
Common situations: Policy configs blocking webmail, social, or internal domains; agents embedding a blocked link in otherwise benign task prose; stale config still blocking a domain a team now needs.
Related errors
- domain '{}' is not in browser_delegate allowed_domains
- Host '{host}' is not in browser.allowed_domains
- memory write blocked by content scan: {kinds}
- unsupported URL scheme: {}
- purge_session not supported by this memory backend
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/23a1af17669e6c6b.
Report an issue: GitHub.