{"record":{"id":"502779f9d1aac9fd","repo":"tinyhumansai/openhuman","slug":"voice-provider-slug-has-no-api-credential-conf","errorCode":null,"errorMessage":"voice provider '{slug}' has no API credential configured","messagePattern":"voice provider '(.+?)' has no API credential configured","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/voice/ops.rs","lineNumber":50,"sourceCode":"    // resolves to a provider: the backend proxy always does, a third-party slug\n    // only when its `voice_providers` entry exists. Constructing the provider is\n    // the same check the transcribe path performs, and it makes no network call\n    // — so this reports the real failure a user would hit, not a guess.\n    let stt_engine = effective_stt_provider(config);\n    let stt_error = create_stt_provider(&stt_engine, \"\", config)\n        .and_then(|_| {\n            let slug = stt_engine\n                .split_once(':')\n                .map_or(stt_engine.as_str(), |(slug, _)| slug);\n            if matches!(slug.trim(), \"cloud\" | \"openhuman\" | \"backend\") {\n                return Ok(());\n            }\n            let key = crate::openhuman::inference::provider::factory::lookup_key_for_slug(\n                slug.trim(),\n                config,\n            )?;\n            if key.trim().is_empty() {\n                anyhow::bail!(\"voice provider '{slug}' has no API credential configured\")\n            }\n            Ok(())\n        })\n        .err()\n        .map(|e| e.to_string());\n    let stt_available = stt_error.is_none();\n    let tts_available = piper_bin.is_some() && tts_voice.is_some();\n\n    debug!(\n        \"{LOG_PREFIX} stt_available={stt_available} stt_engine={stt_engine} \\\n         tts_available={tts_available} piper_bin={} tts_voice={} stt_error={:?}\",\n        safe_basename_path(&piper_bin),\n        safe_basename_str(&tts_voice),\n        stt_error,\n    );\n\n    let tts_provider = if config.local_ai.tts_provider.trim().is_empty() {\n        \"cloud\".to_string()","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/voice/ops.rs#L32-L68","documentation":"voice_status constructs the configured STT provider as a probe and that construction failed for a third-party slug because no matching API credential exists in voice_providers; the status call reports the engine as unavailable for exactly this reason.","triggerScenarios":"Thrown at src/openhuman/voice/ops.rs:50 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Configure credentials for the provider in the voice_providers config section","Switch voice_server.stt_engine to 'backend' to use the backend proxy","Set a valid stt_provider routing string if the slug is stale"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}