tonhowtf/omniget · warning · anyhow::Error
nao esta gravando
Error message
nao esta gravando
What it means
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.
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().
Example fix
// before
dictation::stop(progress).await?;
// after
if is_recording() {
dictation::stop(progress).await?;
} Defensive patterns
Strategy: validation
Validate before calling
// only stop when a session is active
if phase() == "recording" {
dictation::stop(progress).await?;
} Try / catch
match dictation::stop(progress).await {
Err(e) if e.to_string().contains("nao esta gravando") => return Ok(String::new()),
other => other?,
} Prevention
- 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().
When it happens
Trigger: Calling dictation::stop() without a prior successful start(), or after stop() already ran once.
Common situations: 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.
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/232360840ca549ac.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/dictation.rs:267
.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(),
});
set_phase("recording");
*LAST.lock().unwrap_or_else(|e| e.into_inner()) = None;
super::report(&progress, "dictation", "recording", 0, None, None);
Ok(())
}
/// Para a gravação, transcreve e entrega o texto. Devolve o texto.
pub async fn stop(progress: super::ProgressFn) -> anyhow::Result<String> {
let rec = REC.lock().unwrap_or_else(|e| e.into_inner()).take();
let Some(mut rec) = rec else {
return Err(anyhow!("nao esta gravando"));
};
set_phase("transcribing");
super::report(&progress, "dictation", "transcribing", 0, None, None);
let result: anyhow::Result<String> = async {
if rec.started.elapsed().as_millis() < 400 {
tokio::time::sleep(std::time::Duration::from_millis(400)).await;
}
// 'q' no stdin encerra o ffmpeg fechando o WAV corretamente.
if let Some(mut stdin) = rec.child.stdin.take() {
use tokio::io::AsyncWriteExt;
let _ = stdin.write_all(b"q\n").await;
let _ = stdin.flush().await;
}
let status =
tokio::time::timeout(std::time::Duration::from_secs(8), rec.child.wait()).await;
if status.is_err() {
let _ = rec.child.kill().await;
}View on GitHub (pinned to 8600b91f42)