Hmbown/CodeWhale · error
initialized audio cursor
Error message
initialized audio cursor
What it means
Panic from `.expect("initialized audio cursor")` on `audio_cursor.as_mut()` in the pet-watch worker's Advance command handler (crates/tui/src/tui/pet_watch/worker.rs:248). The cursor is guaranteed `Some` at that point because the branch directly above constructs it when it is `None` or when the audio target's stream changed. The expect documents an internal invariant: any `None` here means the initialization logic above was skipped or reordered.
Solutions
- Restore the invariant: the cursor must be constructed immediately before use whenever `audio.filter(Target::active)` yields a target.
- Replace the Option dance with a definite value — e.g. `let cursor = audio_cursor.take().unwrap_or_else(|| AudioCursor{...})` recomputed from the target — eliminating the expect.
- If the panic reproduces on stock code, file a bug with the command sequence; it is a genuine invariant violation.
- Add a comment or debug_assert linking the construction branch and this expect so future edits keep them together.
Example fix
// before
if audio_cursor.as_mut().expect("initialized audio cursor").present(&ctx, &target, frame.time_ms).is_err() { ... }
// after
let cursor = audio_cursor.get_or_insert_with(|| AudioCursor { target: target.clone(), sample: 0, voices: Vec::new() });
if cursor.present(&ctx, &target, frame.time_ms).is_err() { ... } Defensive patterns
Strategy: type-guard
Type guard
let Some(cursor) = audio_cursor.as_mut() else {
unreachable!("audio cursor constructed in branch above whenever target is active");
}; Try / catch
let cursor = audio_cursor.get_or_insert_with(|| AudioCursor { target: target.clone(), sample: 0, voices: Vec::new() });
if cursor.present(&ctx, &target, frame.time_ms).is_err() { /* fail target */ } Prevention
- Keep cursor construction and use in the same branch; do not insert early returns between them.
- Prefer get_or_insert_with over Option + expect for just-in-time invariants.
- Add a debug_assert documenting the pairing when refactoring this arm.
When it happens
Trigger: Only from refactoring: moving the `is_none_or(|c| !c.target.same_stream(&target))` initialization branch away from the `present(...)` call, changing `Target::active` filtering so the branch is bypassed while audio is still active, or introducing an early `continue`/`return` between construction and use.
Common situations: A code review change touching the Advance arm of `run()`; merging conflicting edits to the audio-cursor lifecycle; mistakenly treating the `else { audio_cursor = None }` reset as also covering the active-audio path.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- bounded to model output cap
- event recovery buffer fits u64
- event transaction runs once
- finite points
- provider authority checked above
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/6d9217357a74dae0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/worker.rs:248
frame.host_time_ms = time_ms;
if let Some(target) = audio.filter(Target::active) {
*deadline.lock().map_err(|_| rquickjs::Error::Unknown)? =
Instant::now() + Duration::from_millis(500);
if audio_cursor
.as_ref()
.is_none_or(|c| !c.target.same_stream(&target))
{
audio_cursor = Some(AudioCursor {
target: target.clone(),
sample: (frame.time_ms * audio::SAMPLE_RATE as f64 / 1000.0)
.floor()
as usize,
voices: Vec::new(),
});
}
if audio_cursor
.as_mut()
.expect("initialized audio cursor")
.present(&ctx, &target, frame.time_ms)
.is_err()
{
// Sound failure cannot stop telemetry or its recording.
let _ = ctx.catch();
target.fail();
audio_cursor = None;
}
*deadline.lock().map_err(|_| rquickjs::Error::Unknown)? =
Instant::now() + Duration::from_secs(5);
} else {
audio_cursor = None;
}
if let Ok(mut slot) = output.lock() {
*slot = Some(Ok(frame));
}
}
}View on GitHub (pinned to 73e0f67d83)