Hmbown/CodeWhale · error
Browser login is only enabled for the canonical Codewhale…
Error message
Browser login is only enabled for the canonical Codewhale account API or a loopback test API
What it means
Raised by `validate_verification_url` in crates/cli/src/cloud.rs:1078 when the configured Codewhale account API base is neither the canonical `https://api.codewhale.net:443` nor a loopback host. Browser-based device login is hard-disabled for any other API endpoint, so the login flow refuses before validating the verification URL's origin.
Solutions
- Unset the custom API base (remove CODEWHALE_API_BASE / reset config) so login uses the canonical https://api.codewhale.net
- If testing locally, change the API base to a loopback address such as http://127.0.0.1:8080, which the login flow explicitly supports
- For a remote staging environment, use a non-browser login path (e.g. paste an API key) instead of the device login flow
- Verify the api_base string parses to scheme https, host api.codewhale.net, default port 443
Example fix
// before export CODEWHALE_API_BASE=https://api.staging.example.com codewhale login // after unset CODEWHALE_API_BASE codewhale login
Defensive patterns
Strategy: validation
Validate before calling
fn api_base_allows_browser_login(api_base: &str) -> bool {
url::Url::parse(api_base).map(|api| {
let canonical = api.scheme() == "https"
&& api.host_str() == Some("api.codewhale.net")
&& api.port_or_known_default() == Some(443);
let loopback = api.host_str()
.map(|h| h.eq_ignore_ascii_case("localhost")
|| h.parse::<std::net::IpAddr>().is_ok_and(|a| a.is_loopback()))
.unwrap_or(false);
canonical || loopback
}).unwrap_or(false)
} Type guard
fn is_canonical_or_loopback_api(api_base: &str) -> bool {
api_base_allows_browser_login(api_base)
} Try / catch
match run_device_login() {
Err(e) if e.to_string().contains("only enabled for the canonical") => {
eprintln!("Browser login disabled for this API base; use an API key or switch to api.codewhale.net / localhost.");
}
other => return other,
} Prevention
- Check CODEWHALE_API_BASE and config before interactive login
- Use API-key auth for staging/self-hosted endpoints instead of browser login
- Keep browser login limited to canonical or loopback API bases by policy
- Clear leftover env overrides between environments
When it happens
Trigger: Running `codewhale login` (device/browser flow) with CODEWHALE_API_BASE (or config api_base) set to a remote non-canonical host, e.g. `https://api.staging.example.com` or an internal corporate gateway — anything that is not api.codewhale.net on 443 and not localhost/127.x/::1.
Common situations: Developers pointing the CLI at a staging or self-hosted Codewhale API and attempting interactive browser login; a proxy URL typo; environment variable left over from an earlier test session.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Model defaults belong to config.toml. Use /model and choose…
- 127
- A pinned task provider requires an explicit model
- active provider has no native web-search adapter
- agent profile reasoning_effort must be one of: inherit…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/06855256eea4df4e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/cloud.rs:1078
let api = Url::parse(api_base).context("invalid Codewhale account API base URL")?;
let canonical_api = api.scheme() == "https"
&& api.host_str() == Some("api.codewhale.net")
&& api.port_or_known_default() == Some(443);
let loopback_api = api.host_str().is_some_and(is_loopback_host);
if canonical_api {
if url.scheme() != "https"
|| !host.eq_ignore_ascii_case("app.codewhale.net")
|| url.port_or_known_default() != Some(443)
{
bail!("The Codewhale service returned an untrusted verification origin");
}
} else if loopback_api {
if !matches!(url.scheme(), "http" | "https") || !is_loopback_host(host) {
bail!("The Codewhale service returned an untrusted verification origin");
}
} else {
bail!(
"Browser login is only enabled for the canonical Codewhale account API or a loopback test API"
);
}
let query = url.query_pairs().collect::<Vec<_>>();
if complete {
if query.len() != 1 || query[0].0 != "user_code" || query[0].1 != user_code {
bail!("The Codewhale service returned an unsafe verification URL");
}
} else if !query.is_empty() {
bail!("The Codewhale service returned an unsafe verification URL");
}
Ok(url.to_string())
}
fn is_loopback_host(host: &str) -> bool {
let host = host
.strip_prefix('[')View on GitHub (pinned to 73e0f67d83)