{"record":{"id":"b915724ebd2db744","repo":"Hmbown/CodeWhale","slug":"invalid-session-id-id","errorCode":null,"errorMessage":"Invalid session id '{id}'","messagePattern":"Invalid session id '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/session_manager.rs","lineNumber":1277,"sourceCode":"    /// session without consulting the model transcript.\n    #[cfg_attr(not(test), expect(dead_code))]\n    pub(crate) fn replay_approvals(&self, session_id: &str) -> io::Result<ApprovalReplay> {\n        self.approval_receipt_store().replay(session_id)\n    }\n\n    fn validated_session_id<'a>(&self, id: &'a str) -> std::io::Result<&'a str> {\n        let trimmed = id.trim();\n        if trimmed.is_empty() {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                \"Session id cannot be empty\",\n            ));\n        }\n        if !trimmed\n            .chars()\n            .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')\n        {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                format!(\"Invalid session id '{id}'\"),\n            ));\n        }\n        if trimmed == SESSION_BOOT_OWNERS_STEM {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                format!(\"Session id '{trimmed}' collides with a reserved sessions file\"),\n            ));\n        }\n        Ok(trimmed)\n    }\n\n    fn validated_session_path(&self, id: &str) -> std::io::Result<PathBuf> {\n        let trimmed = self.validated_session_id(id)?;\n        Ok(self.sessions_dir.join(format!(\"{trimmed}.json\")))\n    }\n","sourceCodeStart":1259,"sourceCodeEnd":1295,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/session_manager.rs#L1259-L1295","documentation":"validated_session_id enforces that a session id contains only ASCII alphanumerics, hyphens, and underscores; anything else yields InvalidInput with 'Invalid session id'. This keeps ids safe to embed directly into file names across filesystems. The error names the offending id (after trimming) so the caller can see the disallowed characters.","triggerScenarios":"Calling validated_session_id (or any API that routes through it, like checkpoint_path) with an id containing spaces, slashes, dots, unicode, or other punctuation — e.g. a UUID with braces or a user-typed name like 'my session/1'.","commonSituations":"Passing an untrimmed path fragment as an id; deriving ids from free-form user input or foreign /load file names; using a UUID variant with curly braces or colons.","solutions":["Sanitize the id: replace invalid characters with '-' or '_' before use","Use a generator that produces conforming ids (alphanumeric/hyphen/underscore), e.g. a plain UUID without braces","Show the user the allowed character set when accepting ids interactively"],"exampleFix":"// before\nlet id = format!(\"{}\", uuid);\n// after\nlet id: String = uuid.simple().to_string(); // plain hex, no braces/hyphens\n// or sanitize: id.chars().map(|c| if c.is_ascii_alphanumeric() || c=='-' || c=='_' { c } else { '_' }).collect()","handlingStrategy":"validation","validationCode":"let ok = id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') && !id.trim().is_empty();\nif !ok { return Err(anyhow!(\"id must be [A-Za-z0-9_-]\")); }","typeGuard":"fn is_safe_session_id(id: &str) -> bool {\n    !id.trim().is_empty()\n        && id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')\n}","tryCatchPattern":"match manager.checkpoint_path(&user_id) {\n    Ok(p) => use(p),\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => eprintln!(\"Invalid id '{}': use A-Z a-z 0-9 - _ only\", user_id),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Sanitize free-form user input into the allowed charset before passing it as an id","Use brace-free UUIDs (uuid.simple()) rather than braced forms","Document the id charset wherever users can name sessions"],"tags":["validation","session-id","identifier","rust"],"backgroundTag":"invalid-identifier-format","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"}