{"record":{"id":"232360840ca549ac","repo":"tonhowtf/omniget","slug":"nao-esta-gravando","errorCode":null,"errorMessage":"nao esta gravando","messagePattern":"nao esta gravando","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"src-tauri/omniget-core/src/core/tools/dictation.rs","lineNumber":267,"sourceCode":"        .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(),\n    });\n    set_phase(\"recording\");\n    *LAST.lock().unwrap_or_else(|e| e.into_inner()) = None;\n    super::report(&progress, \"dictation\", \"recording\", 0, None, None);\n    Ok(())\n}\n\n/// Para a gravação, transcreve e entrega o texto. Devolve o texto.\npub async fn stop(progress: super::ProgressFn) -> anyhow::Result<String> {\n    let rec = REC.lock().unwrap_or_else(|e| e.into_inner()).take();\n    let Some(mut rec) = rec else {\n        return Err(anyhow!(\"nao esta gravando\"));\n    };\n    set_phase(\"transcribing\");\n    super::report(&progress, \"dictation\", \"transcribing\", 0, None, None);\n    let result: anyhow::Result<String> = async {\n        if rec.started.elapsed().as_millis() < 400 {\n            tokio::time::sleep(std::time::Duration::from_millis(400)).await;\n        }\n        // 'q' no stdin encerra o ffmpeg fechando o WAV corretamente.\n        if let Some(mut stdin) = rec.child.stdin.take() {\n            use tokio::io::AsyncWriteExt;\n            let _ = stdin.write_all(b\"q\\n\").await;\n            let _ = stdin.flush().await;\n        }\n        let status =\n            tokio::time::timeout(std::time::Duration::from_secs(8), rec.child.wait()).await;\n        if status.is_err() {\n            let _ = rec.child.kill().await;\n        }","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/dictation.rs#L249-L285","documentation":"State guard in dictation stop: the REC session mutex is empty because stop() was called while no recording is active (never started, already stopped, or crashed), so there is no ffmpeg child to terminate or WAV to transcribe.","triggerScenarios":"Calling dictation::stop() without a prior successful start(), or after stop() already ran once.","commonSituations":"User releases the push-to-talk key without having pressed it; double stop() invocation from racing UI events; a previous start() failed with 'ja esta gravando'/'ffmpeg: {}' so no session was registered.","solutions":["Only call stop() when a recording is known active (track the phase or an is_recording flag).","Ignore or no-op a stop() when the phase is not 'recording' instead of surfacing the error.","If start() failed, do not proceed to stop()."],"exampleFix":"// before\ndictation::stop(progress).await?;\n// after\nif is_recording() {\n    dictation::stop(progress).await?;\n}","handlingStrategy":"validation","validationCode":"// only stop when a session is active\nif phase() == \"recording\" {\n    dictation::stop(progress).await?;\n}","typeGuard":null,"tryCatchPattern":"match dictation::stop(progress).await {\n    Err(e) if e.to_string().contains(\"nao esta gravando\") => return Ok(String::new()),\n    other => other?,\n}","preventionTips":["Bind stop() only to a verified active session (push-to-talk release after confirmed press).","Track session state locally; treat stop() as idempotent in the UI layer.","Never call stop() after a failed start()."],"tags":["dictation","audio-recording","invalid-state-transition"],"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"}