aaif-goose/goose · error

Local Whisper model not configured

Error message

Local Whisper model not configured

What it means

Local dictation (feature `local-inference`) reads the Whisper model id from the goose config key LOCAL_WHISPER_MODEL (whisper.rs:11). transcribe_local fails with this error when that key is missing or its value is not a string — before any model lookup or transcription starts.

Source

Thrown at crates/goose/src/dictation/providers.rs:142

            .and_then(|v| v.as_str().map(|s| s.to_string()))
            .and_then(|id| super::whisper::get_model(&id))
            .is_some_and(|m| m.is_downloaded()),
        _ => {
            let def = get_provider_def(provider);
            config.get_secret::<String>(def.config_key).is_ok()
        }
    }
}

#[cfg(feature = "local-inference")]
pub async fn transcribe_local(audio_bytes: Vec<u8>) -> Result<String> {
    tokio::task::spawn_blocking(move || {
        let config = Config::global();
        let model_id = config
            .get(LOCAL_WHISPER_MODEL_CONFIG_KEY, false)
            .ok()
            .and_then(|v| v.as_str().map(|s| s.to_string()))
            .ok_or_else(|| anyhow::anyhow!("Local Whisper model not configured"))?;

        let model = super::whisper::get_model(&model_id)
            .ok_or_else(|| anyhow::anyhow!("Unknown model: {}", model_id))?;
        let model_path = model.local_path();

        let mut transcriber_lock = LOCAL_TRANSCRIBER
            .lock()
            .map_err(|e| anyhow::anyhow!("Failed to lock transcriber: {}", e))?;

        let model_path_str = model_path.to_string_lossy().to_string();
        let needs_reload = match transcriber_lock.as_ref() {
            None => true,
            Some((cached_path, _)) => cached_path != &model_path_str,
        };

        if needs_reload {
            tracing::info!("Loading Whisper model from: {}", model_path.display());

View on GitHub (pinned to 3810898a74)

Solutions

  1. Set the key to one of the supported model ids: tiny, base, small, or medium — e.g. `goose config set --params LOCAL_WHISPER_MODEL tiny`
  2. Use whisper::recommend_model() to pick an id matching your hardware (small on GPU/Metal, base/tiny on CPU)
  3. Confirm with is_configured(DictationProvider::Local) before invoking transcription

Example fix

# before
# LOCAL_WHISPER_MODEL unset -> transcribe_local() errors

# after
goose config set --params LOCAL_WHISPER_MODEL small
Defensive patterns

Strategy: validation

Validate before calling

use goose::config::Config;
use goose::dictation::whisper::LOCAL_WHISPER_MODEL_CONFIG_KEY;

fn local_whisper_ready() -> bool {
    Config::global()
        .get(LOCAL_WHISPER_MODEL_CONFIG_KEY, false)
        .ok()
        .and_then(|v| v.as_str().map(|s| s.to_string()))
        .is_some()
}

// or use the built-in readiness check that also verifies the model is downloaded:
// is_configured(DictationProvider::Local)

Type guard

fn local_whisper_ready() -> bool {
    Config::global()
        .get("LOCAL_WHISPER_MODEL", false)
        .ok()
        .and_then(|v| v.as_str().map(|s| s.to_string()))
        .is_some()
}

Prevention

When it happens

Trigger: Using DictationProvider::Local without ever configuring the model: Config::global().get(LOCAL_WHISPER_MODEL, ...) returns None or a non-string value, so the `.ok()`/`as_str()` chain yields None.

Common situations: Fresh installs where local dictation was selected but no model was chosen; the config key was deleted; the value was written as a non-string type (number/bool) in the config file.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/29d6165255b5584c. Report an issue: GitHub.