{"record":{"id":"9c40b44315475ff4","repo":"Hmbown/CodeWhale","slug":"codewhale-account-api-base-url-must-not-contain-a","errorCode":null,"errorMessage":"Codewhale account API base URL must not contain a query or fragment","messagePattern":"Codewhale account API base URL must not contain a query or fragment","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":1021,"sourceCode":"        writeln!(out, \"Plan: {}\", printable(&user.plan))?;\n    }\n    writeln!(out, \"Profile: {}\", printable(profile))?;\n    writeln!(out, \"API: {api_base}\")?;\n    Ok(())\n}\n\nstruct ValidatedApiBase {\n    url: Url,\n    display: String,\n}\n\nfn validate_api_base(value: &str) -> Result<ValidatedApiBase> {\n    let mut url = Url::parse(value.trim()).context(\"invalid Codewhale account API base URL\")?;\n    if !url.username().is_empty() || url.password().is_some() {\n        bail!(\"Codewhale account API base URL must not contain credentials\");\n    }\n    if url.query().is_some() || url.fragment().is_some() {\n        bail!(\"Codewhale account API base URL must not contain a query or fragment\");\n    }\n    if !matches!(url.path(), \"\" | \"/\") {\n        bail!(\"Codewhale account API base URL must be an origin without a path\");\n    }\n    let host = url\n        .host_str()\n        .ok_or_else(|| anyhow!(\"Codewhale account API base URL must include a host\"))?;\n    let allowed = url.scheme() == \"https\" || (url.scheme() == \"http\" && is_loopback_host(host));\n    if !allowed {\n        bail!(\n            \"Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)\"\n        );\n    }\n    url.set_path(\"/\");\n    let display = url.as_str().trim_end_matches('/').to_string();\n    Ok(ValidatedApiBase { url, display })\n}\n","sourceCodeStart":1003,"sourceCodeEnd":1039,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/cli/src/cloud.rs#L1003-L1039","documentation":"The account API base URL must be a bare origin: validate_api_base rejects any URL carrying a query string or fragment, since the client builds /api/... paths itself and a query or fragment would corrupt request URLs or be dropped inconsistently.","triggerScenarios":"Setting the base URL with a trailing ?key=... query or #fragment, e.g. https://api.example.com?env=prod or https://api.example.com/#/dashboard.","commonSituations":"Copying a URL from a browser address bar (often carries query/fragment); adding an API key as a query parameter out of habit.","solutions":["Remove everything from ? or # onward; use only the origin, e.g. https://api.example.com.","Query-parameter auth is unsupported for this endpoint — use header-based auth.","Retry the command with the origin-only URL."],"exampleFix":"// before\nCODEWHALE_API_BASE=https://api.example.com?env=prod\n// after\nCODEWHALE_API_BASE=https://api.example.com","handlingStrategy":"validation","validationCode":"fn is_bare_origin(url: &str) -> bool {\n    url::Url::parse(url.trim())\n        .map(|u| u.query().is_none() && u.fragment().is_none())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Configure origin-only URLs: cut at the first ? or #.","Do not paste URLs straight from a browser address bar.","Never put auth in query parameters for this client.","Validate the base URL in CI config checks before deploy."],"tags":["cli","url","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}