zeroclaw-labs/zeroclaw · error
URL userinfo is not allowed
Error message
URL userinfo is not allowed
What it means
While parsing the authority, extract_host rejects any URL containing '@', i.e. userinfo of the form user:pass@host (browser_open.rs:304-306). This is a deliberate security policy: credentials embedded in the URL would leak into logs, browser history, and the tool-result text. The check is substring-based, so even a bare 'https://@example.com' is rejected.
Source
Thrown at crates/zeroclaw-tools/src/browser_open.rs:305
})?;
let authority = rest.split(['/', '?', '#']).next().ok_or_else(|| {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"url": url})),
"browser_open: invalid URL"
);
anyhow::Error::msg("Invalid URL")
})?;
if authority.is_empty() {
anyhow::bail!("URL must include a host");
}
if authority.contains('@') {
anyhow::bail!("URL userinfo is not allowed");
}
if authority.starts_with('[') {
anyhow::bail!("IPv6 hosts are not supported in browser_open");
}
let host = authority
.split(':')
.next()
.unwrap_or_default()
.trim()
.trim_end_matches('.')
.to_lowercase();
if host.is_empty() {
anyhow::bail!("URL must include a valid host");
}
View on GitHub (pinned to 88bb9c8533)
Solutions
- Remove the userinfo component: use 'https://example.com/path' and authenticate via the target site's own login or OAuth flow after the browser opens.
- Never place secrets in URLs routed through tools; pass them via headers or a secrets store in the calling application instead.
- If an '@' appears unexpectedly, inspect where the URL was assembled (env var, prompt context) and strip the credentials there.
Example fix
// before
let url = format!("https://{user}:{pass}@api.example.com/v1");
// after: no credentials in the URL; the browser login flow handles auth
let url = "https://api.example.com/v1".to_string(); Defensive patterns
Strategy: validation
Validate before calling
fn url_has_no_userinfo(url: &str) -> bool {
let Some(rest) = url
.strip_prefix("https://")
.or_else(|| url.strip_prefix("http://"))
else {
return false;
};
let authority = rest.split(['/', '?', '#']).next().unwrap_or("");
!authority.contains('@')
} Prevention
- Never embed user:pass@ in URLs sent to tools; pass credentials via headers or the site's auth flow.
- Scrub '@' from prompt-provided URLs before invoking browser_open.
- Treat any '@' before the first '/' in a URL as a credential leak in logs and reject it early.
When it happens
Trigger: Calling browser_open with 'https://user:pass@example.com/', 'http://admin@10.0.0.1/', or any URL where an '@' appears before the first '/', '?', or '#'. Common with URLs copied from database-style connection strings or basic-auth test links.
Common situations: Developers pasting authenticated URLs (internal dashboards, basic-auth staging sites) into the agent; LLMs helpfully appending credentials they saw in context; template code injecting an apikey subdomain like 'https://{key}.service.com' only when the key contains '@'.
Related errors
- security.otp.cache_valid_secs must be greater than or equal
- security.otp.gated_actions[{i}] contains invalid characters:
- AgentScopedMemory refuses purge_namespace: cross-agent bulk
- Request timestamp too old or too far in future
- runtime.wasm.tools_dir must not contain '..' path traversal
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/8093696236919700.
Report an issue: GitHub.