{"record":{"id":"e8c27a26e3cd1d3a","repo":"zeroclaw-labs/zeroclaw","slug":"transcription-is-enabled-but-no-transcription-prov","errorCode":null,"errorMessage":"Transcription is enabled but no transcription provider registered successfully. Configure at least one of: [transcription] (Groq) with api_key + api_url; [transcription.openai]; [transcription.deepgram]; [transcription.assemblyai]; [transcription.google]; [transcription.local_whisper]; or [providers.transcription.<type>.<alias>].","messagePattern":"Transcription is enabled but no transcription provider registered successfully\\. Configure at least one of: \\[transcription\\] \\(Groq\\) with api_key \\+ api_url; \\[transcription\\.openai\\]; \\[transcription\\.deepgram\\]; \\[transcription\\.assemblyai\\]; \\[transcription\\.google\\]; \\[transcription\\.local_whisper\\]; or \\[providers\\.transcription\\.<type>\\.<alias>\\]\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/transcription.rs","lineNumber":948,"sourceCode":"        }\n    }\n\n    /// Build a `TranscriptionManager` from a `TranscriptionConfig`. The\n    /// resolved agent alias starts empty; orchestrators that wire the\n    /// manager to a specific agent should call\n    /// `with_agent_transcription_provider` to set it.\n    pub fn new(config: &TranscriptionConfig) -> Result<Self> {\n        if matches!(config.max_audio_bytes, Some(0)) {\n            bail!(\"transcription.max_audio_bytes must be greater than zero\");\n        }\n\n        let mut transcription_providers: HashMap<String, Box<dyn TranscriptionProvider>> =\n            HashMap::new();\n\n        Self::register_legacy_providers(&mut transcription_providers, config);\n\n        if config.enabled && transcription_providers.is_empty() {\n            bail!(\n                \"Transcription is enabled but no transcription provider registered \\\n                 successfully. Configure at least one of: [transcription] (Groq) \\\n                 with api_key + api_url; [transcription.openai]; [transcription.deepgram]; \\\n                 [transcription.assemblyai]; [transcription.google]; [transcription.local_whisper]; \\\n                 or [providers.transcription.<type>.<alias>].\"\n            );\n        }\n\n        Ok(Self {\n            transcription_providers,\n            max_audio_bytes: config.max_audio_bytes,\n            agent_transcription_provider: String::new(),\n        })\n    }\n\n    pub fn from_config_for_agent(config: &Config, agent_alias: Option<&str>) -> Result<Self> {\n        if matches!(config.transcription.max_audio_bytes, Some(0)) {\n            bail!(\"transcription.max_audio_bytes must be greater than zero\");","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/transcription.rs#L930-L966","documentation":"TranscriptionManager::new() bails when transcription is enabled but the legacy provider registration produced zero providers. Registration is best-effort: each provider whose config is missing or invalid (including local_whisper) is silently skipped with a WARN log, and only the final 'is the map empty' check fails loudly. Note that new() registers only the legacy [transcription.*] blocks — typed [providers.transcription.<type>.<alias>] entries are not seen here (use from_config_for_agent or with_typed_providers for those).","triggerScenarios":"transcription.enabled = true while every provider registration returned Err or its config block is absent: no [transcription] Groq api_key/api_url, no openai/deepgram/assemblyai/google/local_whisper blocks, or their configs are invalid (e.g. local_whisper with a bad config — it is skipped with the WARN 'local_whisper config invalid, provider skipped').","commonSituations":"Enabling transcription but supplying the key under a different block name than the provider expects; only [providers.transcription.*] typed config present while constructing via new(); a typo'd api_key field so from_config returns Err and the provider is skipped; local_whisper URL malformed so it never registers.","solutions":["Add at least one complete provider block, e.g. [transcription] with api_key + api_url (Groq) or [transcription.openai] / [transcription.deepgram] / [transcription.local_whisper]","Check startup logs for the WARN lines 'local_whisper config invalid, provider skipped' — they name the exact reason each provider failed to register","If you only use typed [providers.transcription.<type>.<alias>] config, build the manager via from_config_for_agent (or empty().with_typed_providers(...)) instead of new()","If transcription is not actually wanted, set transcription.enabled = false"],"exampleFix":"# before\n[transcription]\nenabled = true\n# no provider blocks -> error\n\n# after\n[transcription]\nenabled = true\napi_key = \"gsk_...\"\napi_url = \"https://api.groq.com/openai/v1/audio/transcriptions\"","handlingStrategy":"validation","validationCode":"// Fail fast with a clearer message than the constructor's\nfn transcription_startup_ok(cfg: &TranscriptionConfig) -> bool {\n    !cfg.enabled || provider_config_present(cfg)\n}\n\nfn provider_config_present(cfg: &TranscriptionConfig) -> bool {\n    cfg.api_key.as_deref().is_some_and(|k| !k.is_empty())\n        || cfg.openai.is_some() || cfg.deepgram.is_some()\n        || cfg.assemblyai.is_some() || cfg.google.is_some()\n        || cfg.local_whisper.is_some()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run config validation before startup and report missing provider blocks explicitly","Watch startup WARN logs — skipped providers explain why the map ends up empty","If you use typed [providers.transcription.*] config only, do not construct via new()"],"tags":["config","transcription","startup","missing-provider"],"backgroundTag":"config-validation-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}