tonhowtf/omniget · warning · anyhow::Error

ja esta gravando

Error message

ja esta gravando

What it means

start() refuses to begin a new dictation session when REC already holds an active Recording. The guard checks the shared mutex-protected slot and returns this Portuguese message ('already recording') so a second overlapping ffmpeg capture is never spawned. It is an intentional state-machine guard, not a bug.

Solutions

  1. Call stop() to finish the current recording before calling start() again.
  2. Debounce/disable the start trigger in the UI while a recording is active (check the phase or expose an is_recording getter).
  3. If the session is stuck (e.g. ffmpeg hung), kill the ffmpeg child process and clear REC, then retry start().

Example fix

// before
let _ = dictation::start(progress).await?;
// after
if !is_recording() {
    dictation::start(progress).await?;
} else {
    // already recording: ignore or surface a UI hint
}
Defensive patterns

Strategy: validation

Validate before calling

// Rust (caller side)
if is_recording() {
    return; // skip start(), already recording
}
dictation::start(progress).await?;

Try / catch

match dictation::start(progress).await {
    Err(e) if e.to_string().contains("ja esta gravando") => { /* already recording: no-op */ }
    other => other?,
}

Prevention

When it happens

Trigger: Calling dictation::start() while a previous start() succeeded and stop() has not yet been called (or is still awaiting).

Common situations: User presses the dictation hotkey twice; frontend double-clicks the mic button without disabling it; a previous stop() failed leaving the recording session stuck in REC.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/f9b0ff7d0f94e304. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/dictation.rs:237

    } else {
        vec![
            "-f".into(),
            "pulse".into(),
            "-i".into(),
            if dev.is_empty() {
                "default".into()
            } else {
                dev.to_string()
            },
        ]
    }
}

pub async fn start(progress: super::ProgressFn) -> anyhow::Result<()> {
    {
        let rec = REC.lock().unwrap_or_else(|e| e.into_inner());
        if rec.is_some() {
            return Err(anyhow!("ja esta gravando"));
        }
    }
    let ffmpeg = crate::core::dependencies::ensure_ffmpeg().await?;
    let opts = options();
    let wav = super::temp_dir().join(format!("dictation-{}.wav", uuid::Uuid::new_v4()));
    let mut cmd = crate::core::process::command(&ffmpeg);
    cmd.args(["-y", "-hide_banner", "-loglevel", "error"]);
    cmd.args(input_args(&opts.device));
    cmd.args(["-ac", "1", "-ar", "16000", "-c:a", "pcm_s16le"])
        .arg(&wav);
    cmd.stdin(std::process::Stdio::piped())
        .stdout(std::process::Stdio::null())
        .stderr(std::process::Stdio::piped());
    let child = cmd.spawn().map_err(|e| anyhow!("ffmpeg: {}", e))?;
    *REC.lock().unwrap_or_else(|e| e.into_inner()) = Some(Recording {
        child,
        wav,
        started: Instant::now(),

View on GitHub (pinned to 8600b91f42)