{"record":{"id":"649cf5e0d257f159","repo":"Hmbown/CodeWhale","slug":"custom-endpoint-credentials-for-route-name-must","errorCode":null,"errorMessage":"Custom endpoint credentials for {route_name} must be bound explicitly. Ambient provider credentials are not sent to {}. Add api_key or api_key_env to this provider route, or pass --api-key with --base-url.","messagePattern":"Custom endpoint credentials for (.+?) must be bound explicitly\\. Ambient provider credentials are not sent to (.+?)\\. Add api_key or api_key_env to this provider route, or pass --api-key with --base-url\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":7313,"sourceCode":"        // The Codewhale API always authenticates. It is not a self-hosted\n        // runtime, and a loopback `CODEWHALE_API_BASE` is a test origin for\n        // the same authenticated contract — never a keyless one. Without this\n        // the loopback arm below returned an empty key and the request went\n        // out with no `Authorization` header at all.\n        if provider != ApiProvider::Codewhale\n            && !auth_mode_requires_api_key(auth_mode.as_deref())\n            && (provider_route_is_keyless_self_hosted(provider, &self.active_route_base_url())\n                || base_url_uses_local_host(&self.active_route_base_url()))\n        {\n            return Ok(String::new());\n        }\n\n        if custom_endpoint {\n            let route_name = self\n                .provider\n                .as_deref()\n                .unwrap_or_else(|| provider.as_str());\n            anyhow::bail!(\n                \"Custom endpoint credentials for {route_name} must be bound explicitly. Ambient provider credentials are not sent to {}. Add api_key or api_key_env to this provider route, or pass --api-key with --base-url.\",\n                self.active_route_base_url()\n            );\n        }\n\n        match provider {\n            ApiProvider::Codewhale => anyhow::bail!(\n                \"Codewhale API key not found, so no request was sent.\\n\\\n                 \\n\\\n                 The Codewhale API authenticates every model with one account \\\n                 API key carrying the `models:infer` scope.\\n\\\n                 \\n\\\n                 1. Create one and save it on this machine:\\n\\\n                        codewhale account api-keys create --name <name> --scope models:infer --use\\n\\\n                 2. Or export it for this shell:\\n\\\n                        export CODEWHALE_API_KEY=cwc_key_...\\n\\\n                 \\n\\\n                 You can also create a key at {} and put it in \\","sourceCodeStart":7295,"sourceCodeEnd":7331,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/config.rs#L7295-L7331","documentation":"Custom (--base-url / custom endpoint) provider routes must carry their own explicit credentials. Codewhale will not forward ambient provider credentials (keys meant for the official provider endpoint) to a user-supplied custom base URL, so the route fails at config resolution before any request leaves the machine.","triggerScenarios":"A provider route is marked custom_endpoint (custom base URL via [providers.<name>] base_url or --base-url) while its config has neither api_key nor api_key_env and no --api-key was passed.","commonSituations":"Pointing an OpenAI-compatible proxy/gateway at codewhale but forgetting to attach the proxy's key; moving a working official-provider config onto a self-hosted relay and assuming the existing ambient key still applies.","solutions":["Add api_key = \"...\" to the [providers.<route_name>] table in ~/.codewhale/config.toml.","Or add api_key_env = \"YOUR_ENV_VAR\" to the route and export that variable.","Or pass --api-key together with --base-url on the command line."],"exampleFix":"// before\n[providers.my-proxy]\nbase_url = \"https://internal.corp/v1\"\n\n// after\n[providers.my-proxy]\nbase_url = \"https://internal.corp/v1\"\napi_key_env = \"MY_PROXY_KEY\"","handlingStrategy":"validation","validationCode":"fn custom_endpoint_has_creds(p: &ProviderCfg) -> Result<(), String> {\n    if p.base_url.is_some() && p.api_key.is_none() && p.api_key_env.is_none() {\n        Err(format!(\"provider '{}' has a custom base_url but no api_key/api_key_env\", p.name))\n    } else { Ok(()) }\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"must be bound explicitly\") => {\n        eprintln!(\"add api_key or api_key_env to this route, or pass --api-key with --base-url\");\n    }\n    other => other?,\n}","preventionTips":["Always pair a custom --base-url with an explicit api_key or api_key_env in the same route.","Never assume ambient provider keys follow a request to a custom endpoint — that is blocked by design.","Smoke-test new custom routes with one cheap request before relying on them."],"tags":["config","security","custom-endpoint","credentials"],"backgroundTag":"missing-credentials","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"}