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

  1. Restore the invariant: the cursor must be constructed immediately before use whenever `audio.filter(Target::active)` yields a target.
  2. 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.
  3. If the panic reproduces on stock code, file a bug with the command sequence; it is a genuine invariant violation.
  4. 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

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


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)