{"record":{"id":"e692be1527b694fd","repo":"Hmbown/CodeWhale","slug":"session-should-exist-after-touch","errorCode":null,"errorMessage":"session should exist after touch","messagePattern":"session should exist after touch","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/web_run.rs","lineNumber":141,"sourceCode":"            && let Some(oldest_namespace) = self\n                .sessions\n                .iter()\n                .min_by_key(|(_, session)| session.last_access)\n                .map(|(existing_namespace, _)| existing_namespace.clone())\n        {\n            self.remove_session(&oldest_namespace);\n        }\n\n        let session = self.sessions.entry(namespace.to_string()).or_default();\n        session.last_access = Instant::now();\n    }\n\n    fn next_turn(&mut self, namespace: &str) -> u64 {\n        self.touch_session(namespace);\n        let session = self\n            .sessions\n            .get_mut(namespace)\n            .expect(\"session should exist after touch\");\n        let current = session.next_turn;\n        session.next_turn = session.next_turn.saturating_add(1);\n        current\n    }\n\n    fn store_page(&mut self, namespace: &str, ref_id: &str, page: WebPage) {\n        self.touch_session(namespace);\n        let mut evicted_refs = Vec::new();\n        {\n            let session = self\n                .sessions\n                .get_mut(namespace)\n                .expect(\"session should exist after touch\");\n            if let Some(existing_idx) = session.refs.iter().position(|existing| existing == ref_id)\n            {\n                session.refs.remove(existing_idx);\n            }\n            session.refs.push_back(ref_id.to_string());","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/web_run.rs#L123-L159","documentation":"next_turn calls touch_session(namespace) immediately before fetching the session, and touch_session guarantees insertion via entry().or_default(). This expect therefore only fires if the session map was concurrently mutated or the insert logic regressed — an internal invariant break in WebRunSessionStore, not a user-input problem.","triggerScenarios":"Thrown at crates/tui/src/tools/web_run.rs:141 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Keep touch_session and the sessions.get call adjacent so no code can remove the entry in between","Replace the expect with a defensive or_default() insert if future changes interleave eviction logic","Add a unit test that next_turn after touch_session always finds the namespace"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}