{"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":"console","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":6346,"sourceCode":"                        \"antigravity credential plane did not yield a sendable token\"\n                    );\n                }\n            }\n        }\n\n        if !auth_mode_requires_api_key(auth_mode.as_deref())\n            && (provider_route_is_keyless_self_hosted(provider, &self.deepseek_base_url())\n                || base_url_uses_local_host(&self.deepseek_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.deepseek_base_url()\n            );\n        }\n\n        match provider {\n            ApiProvider::Deepseek | ApiProvider::DeepseekCN => anyhow::bail!(\n                \"DeepSeek API key not found.\\n\\\n                 \\n\\\n                 1. Get a key:  https://platform.deepseek.com/api_keys\\n\\\n                 2. Save it (works in every folder, no OS prompts):\\n\\\n                        codewhale auth set --provider deepseek\\n\\\n                 \\n\\\n                 Alternatives:\\n\\\n                   • export DEEPSEEK_API_KEY=<your-key>      (current shell only;\\n\\\n                     also note: zsh users — exports in ~/.zshrc only reach interactive\\n\\\n                     shells, prefer ~/.zshenv for everything)\\n\\\n                   • api_key = \\\"<your-key>\\\"  in ~/.codewhale/config.toml\\n\\","sourceCodeStart":6328,"sourceCodeEnd":6364,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/config.rs#L6328-L6364","documentation":"When a custom endpoint (base_url override) is used on a non-loopback host and the route is not keyless-self-hosted, key resolution refuses to fall through: ambient provider credentials are intentionally never sent to a custom host. The route must bind its own api_key/api_key_env, or the caller must pass --api-key alongside --base-url.","triggerScenarios":"Launching with --base-url https://api.example.com/v1 without --api-key while [providers.<route>] has neither api_key nor api_key_env; pointing a custom route at a remote gateway expecting the ambient ANTHROPIC/DEEPSEEK key to be reused.","commonSituations":"Migrating from a first-party provider to an OpenAI-compatible gateway and assuming env credentials carry over; typos in base_url turning a loopback plan into a remote one; proxy front-ends that require their own keys.","solutions":["Add api_key or api_key_env to the [providers.<route>] table for that custom route.","Pass --api-key together with --base-url on the command line.","If the endpoint truly needs no auth, host it on a loopback address so the keyless self-hosted path applies."],"exampleFix":"# before\ncodewhale --provider myroute --base-url https://api.example.com/v1\n\n# after\ncodewhale --provider myroute --base-url https://api.example.com/v1 --api-key sk-...\n# or in config:\n# [providers.myroute]\n# base_url = \"https://api.example.com/v1\"\n# api_key = \"sk-...\"","handlingStrategy":"validation","validationCode":"// preflight: a custom endpoint on a non-loopback host must carry explicit credentials\nfn custom_endpoint_credentials_bound(cfg: &Config, route: &ProviderConfig) -> bool {\n    route.api_key.is_some()\n        || route.api_key_env.is_some()\n        || base_url_uses_local_host(&cfg.deepseek_base_url())\n}\n\nanyhow::ensure!(\n    custom_endpoint_credentials_bound(&config, route),\n    \"custom endpoint needs api_key/api_key_env or --api-key\"\n);","typeGuard":"fn custom_route_needs_key(base_url: &str) -> bool {\n    !base_url_uses_local_host(&base_url.to_string())\n}","tryCatchPattern":"if let Err(e) = config.deepseek_api_key() {\n    if e.to_string().starts_with(\"Custom endpoint credentials for\") {\n        // prompt for --api-key or an [providers.<route>] api_key entry; never forward ambient creds\n        return Err(e);\n    }\n    return Err(e);\n}","preventionTips":["Never assume ambient provider env keys reach a custom base_url — bind credentials per route.","Pair --base-url with --api-key in scripts and fail fast if either is missing.","Reserve keyless operation for loopback self-hosted endpoints."],"tags":["rust","custom-provider","api-key","base-url","security","config"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}