{"record":{"id":"f9b0ff7d0f94e304","repo":"tonhowtf/omniget","slug":"ja-esta-gravando","errorCode":null,"errorMessage":"ja esta gravando","messagePattern":"ja esta gravando","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"src-tauri/omniget-core/src/core/tools/dictation.rs","lineNumber":237,"sourceCode":"    } else {\n        vec![\n            \"-f\".into(),\n            \"pulse\".into(),\n            \"-i\".into(),\n            if dev.is_empty() {\n                \"default\".into()\n            } else {\n                dev.to_string()\n            },\n        ]\n    }\n}\n\npub async fn start(progress: super::ProgressFn) -> anyhow::Result<()> {\n    {\n        let rec = REC.lock().unwrap_or_else(|e| e.into_inner());\n        if rec.is_some() {\n            return Err(anyhow!(\"ja esta gravando\"));\n        }\n    }\n    let ffmpeg = crate::core::dependencies::ensure_ffmpeg().await?;\n    let opts = options();\n    let wav = super::temp_dir().join(format!(\"dictation-{}.wav\", uuid::Uuid::new_v4()));\n    let mut cmd = crate::core::process::command(&ffmpeg);\n    cmd.args([\"-y\", \"-hide_banner\", \"-loglevel\", \"error\"]);\n    cmd.args(input_args(&opts.device));\n    cmd.args([\"-ac\", \"1\", \"-ar\", \"16000\", \"-c:a\", \"pcm_s16le\"])\n        .arg(&wav);\n    cmd.stdin(std::process::Stdio::piped())\n        .stdout(std::process::Stdio::null())\n        .stderr(std::process::Stdio::piped());\n    let child = cmd.spawn().map_err(|e| anyhow!(\"ffmpeg: {}\", e))?;\n    *REC.lock().unwrap_or_else(|e| e.into_inner()) = Some(Recording {\n        child,\n        wav,\n        started: Instant::now(),","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/dictation.rs#L219-L255","documentation":"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.","triggerScenarios":"Calling dictation::start() while a previous start() succeeded and stop() has not yet been called (or is still awaiting).","commonSituations":"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.","solutions":["Call stop() to finish the current recording before calling start() again.","Debounce/disable the start trigger in the UI while a recording is active (check the phase or expose an is_recording getter).","If the session is stuck (e.g. ffmpeg hung), kill the ffmpeg child process and clear REC, then retry start()."],"exampleFix":"// before\nlet _ = dictation::start(progress).await?;\n// after\nif !is_recording() {\n    dictation::start(progress).await?;\n} else {\n    // already recording: ignore or surface a UI hint\n}","handlingStrategy":"validation","validationCode":"// Rust (caller side)\nif is_recording() {\n    return; // skip start(), already recording\n}\ndictation::start(progress).await?;","typeGuard":null,"tryCatchPattern":"match dictation::start(progress).await {\n    Err(e) if e.to_string().contains(\"ja esta gravando\") => { /* already recording: no-op */ }\n    other => other?,\n}","preventionTips":["Track recording state in the UI and disable the start trigger while active.","Always pair start() with a stop(), including on error/cancel paths.","Expose an is_recording/phase getter and check it before start()."],"tags":["dictation","audio-recording","invalid-state-transition","concurrency"],"backgroundTag":"invalid-state-transition","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}