{"record":{"id":"7f7de60ed353f671","repo":"Hmbown/CodeWhale","slug":"turn-monitor-exits-normally-only-after-assigning-a-terminal","errorCode":null,"errorMessage":"turn monitor exits normally only after assigning a terminal status","messagePattern":"turn monitor exits normally only after assigning a terminal status","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":12888,"sourceCode":"                        // only when the store still has none — a concurrent\n                        // PUT/DELETE is the newer revision and wins — and\n                        // adopt the revision only when we actually created it,\n                        // so settlement and later checkpoints stay fenced.\n                        if let Some(goal_id) = self\n                            .store\n                            .create_goal_from_snapshot_if_absent(&thread_id, &snapshot)?\n                        {\n                            admitted_goal_id = Some(goal_id);\n                        }\n                    }\n                    latest_goal_snapshot = Some(snapshot);\n                }\n                _ => {}\n            }\n        }\n\n        let mut turn_status = turn_status\n            .expect(\"turn monitor exits normally only after assigning a terminal status\");\n\n        if self\n            .is_interrupt_requested(&thread_id, &turn_id)\n            .await\n            .unwrap_or(false)\n        {\n            turn_status = RuntimeTurnStatus::Interrupted;\n        }\n\n        if let Some(mut item) = current_message_item.take() {\n            item.status = match turn_status {\n                RuntimeTurnStatus::Completed => TurnItemLifecycleStatus::Completed,\n                RuntimeTurnStatus::Interrupted | RuntimeTurnStatus::Canceled => {\n                    TurnItemLifecycleStatus::Interrupted\n                }\n                RuntimeTurnStatus::Queued\n                | RuntimeTurnStatus::InProgress\n                | RuntimeTurnStatus::Failed => TurnItemLifecycleStatus::Failed,","sourceCodeStart":12870,"sourceCodeEnd":12906,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L12870-L12906","documentation":"Panic from `turn_status.expect(...)` at the end of the turn monitor loop. The monitor's exit paths should always assign a terminal status before breaking out of the select loop; reaching the expect with None means the loop ended without ever producing a terminal turn status, indicating an unhandled event path or premature loop exit.","triggerScenarios":"The turn monitor's select loop terminating via a channel close, cancellation, or an unmatched event branch without any branch setting `turn_status` to a Terminal variant.","commonSituations":"Upstream stream ends or errors in a way not mapped to a terminal status; a refactor removing an assignment path in the monitor's match arms.","solutions":["Add a terminal-status assignment to every loop-exit path (including channel closure)","Log unmatched select branches so silent exits become visible","Replace the bare expect with an explicit error status fallback for unexpected exits"],"exampleFix":"// before\nlet mut turn_status = turn_status\n    .expect(\"turn monitor exits normally only after assigning a terminal status\");\n// after\nlet turn_status = turn_status.unwrap_or_else(|| {\n    tracing::error!(\"turn monitor exited without terminal status\");\n    TerminalStatus::error(\"turn monitor exited without terminal status\")\n});","handlingStrategy":"validation","validationCode":null,"typeGuard":"if turn_status.is_none() { tracing::error!(\"monitor exited without terminal status\"); turn_status = Some(TerminalStatus::error(\"unexpected exit\")); }","tryCatchPattern":"let turn_status = turn_status.unwrap_or_else(|| TerminalStatus::error(\"turn monitor exited without terminal status\"));","preventionTips":["Enumerate all loop-exit paths and require a terminal status in each","Treat channel closure / cancellation as terminal-status-producing events","Add a monitor-level test that every exit path yields a status"],"tags":["rust","panic","async","state-machine","invariant"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}