{"record":{"id":"4183a16b5cdff4a4","repo":"tinyhumansai/openhuman","slug":"no-usable-credentials-for-slug-which-openhuma","errorCode":null,"errorMessage":"No usable credentials for '{slug}', which OpenHuman selected for the {} workload. Your chat model is local ('{}') and does not serve this workload, so it fell back to your cloud provider — but '{slug}' has no API key configured. Add a key for '{slug}' in Connections → LLM, set {}_provider to a provider that is configured, or enable the managed OpenHuman backend.","messagePattern":"No usable credentials for '(.+?)', which OpenHuman selected for the (.+?) workload\\. Your chat model is local \\('(.+?)'\\) and does not serve this workload, so it fell back to your cloud provider — but '(.+?)' has no API key configured\\. Add a key for '(.+?)' in Connections → LLM, set (.+?)_provider to a provider that is configured, or enable the managed OpenHuman backend\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/inference/provider/factory.rs","lineNumber":2287,"sourceCode":"    // 401 from the provider several layers later — exactly the baffling error\n    // this diagnostic exists to replace.\n    //\n    // Scoped to the *implicit fallback* path deliberately. That is the case the\n    // diagnostic is for: a local-chat user whose background role landed on a\n    // BYOK slug they never configured. An explicitly routed provider keeps its\n    // existing behaviour and is allowed to build without a stored key — callers\n    // construct such models to probe or describe a provider before a key is\n    // saved, and failing that at construction time would be a behaviour change\n    // well beyond this diagnostic.\n    //\n    // Styles that carry no stored key (`OpenhumanJwt` injects a session JWT\n    // downstream, `None` sends no auth header at all) are legitimately blank and\n    // never trip this.\n    if implicit_fallback\n        && key.trim().is_empty()\n        && matches!(entry.auth_style, AuthStyle::Bearer | AuthStyle::Anthropic)\n    {\n        anyhow::bail!(\"{}\", missing_credentials());\n    }\n    let bearer_is_oauth = slug == \"openai\" && openai_bearer_is_oauth(config);\n    let codex = resolve_openai_codex_routing(config, slug, &entry.endpoint, &key, bearer_is_oauth)\n        .map_err(anyhow::Error::msg)?;\n\n    Ok(CloudSlugResolution {\n        entry,\n        effective_model,\n        key,\n        codex,\n    })\n}\n\n/// A `<slug>:<model>` BYOK cloud provider as a crate-native [`ChatModel`] — the\n/// Native model for every configured cloud auth style, including the managed\n/// `OpenhumanJwt` entry (issue #4727 Phase 3).\n///\n/// Returns `None` unless the role resolves to a **configured** cloud slug. When","sourceCodeStart":2269,"sourceCodeEnd":2305,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/inference/provider/factory.rs#L2269-L2305","documentation":"Implicit-fallback credential failure: the user's chat model is local, so this workload (e.g. a cloud-only role) fell back to a cloud slug that has NO stored API key, and the entry's auth style is Bearer/Anthropic (styles that require a stored key — OpenhumanJwt and None are exempt). The long message (from `missing_credentials()`) explains the local-chat fallback chain and offers three remedies. Triggered at factory.rs:~2287 when `implicit_fallback && key.is_empty()`.","triggerScenarios":"Primary chat set to a local model (Ollama etc.) while a secondary workload (title generation, embeddings, summaries) implicitly falls back to the configured cloud provider slug, whose API key was never entered in Connections → LLM.","commonSituations":"New users who run local chat but never added a cloud key; a key stored under a different slug name than the one configured; keys cleared by a workspace reset.","solutions":["Add an API key for '{slug}' under Connections → LLM in Settings.","Or set the `{role}_provider` for the failing workload to a provider that IS configured (or a local model that serves it).","Or enable the managed OpenHuman backend, which needs no user key.","Verify the slug spelling — a typo'd slug will always look key-less."],"exampleFix":"# settings path: Connections -> LLM -> add key for the slug named in the error\n# or config: point the workload at a configured provider\n# before\nsummary_provider = \"mycustom-slug\"\n# after\nsummary_provider = \"ollama:llama3.1:8b\"","handlingStrategy":"validation","validationCode":"// Before routing a cloud-only workload while chat is local:\nlet key = auth.get_provider_bearer_token(&slug, None)?.unwrap_or_default();\nlet needs_key = matches!(entry.auth_style, AuthStyle::Bearer | AuthStyle::Anthropic);\nif key.trim().is_empty() && needs_key {\n    return prompt_add_key(&slug); // or pick a configured provider\n}","typeGuard":"fn slug_has_usable_key(slug: &str, entry: &CloudProviderEntry, auth: &AuthService) -> bool {\n    if !matches!(entry.auth_style, AuthStyle::Bearer | AuthStyle::Anthropic) { return true; }\n    auth.get_provider_bearer_token(slug, None)\n        .ok()\n        .flatten()\n        .is_some_and(|k| !k.trim().is_empty())\n}","tryCatchPattern":null,"preventionTips":["When chat is local, review every cloud-only background role's provider and key.","Save keys via Connections → LLM immediately after adding a slug.","OpenhumanJwt/None auth styles need no key — misclassifying a slug's style hides this error until request time."],"tags":["credentials","api-key","implicit-fallback","factory"],"backgroundTag":"missing-api-key","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-23T11:17:13.642Z"}