Hmbown/CodeWhale · error · InvalidInput

Session id ' ' collides with a reserved sessions file

Error message

Session id '{trimmed}' collides with a reserved sessions file

What it means

Input validation in validated_session_id (session_manager.rs). After rejecting empty ids and non-alphanumeric characters, the trimmed id is checked against reserved names used by the sessions directory metadata (such as the sessions index file); an id matching a reserved sessions filename is refused with InvalidInput because creating a session with that id would collide with the reserved file on disk and corrupt session storage.

Solutions

  1. Choose a different session id that does not match the reserved stem
  2. Add a distinguishing prefix/suffix (e.g. a timestamp or random token) to generated ids
  3. Check the constant SESSION_BOOT_OWNERS_STEM in session_manager.rs and exclude it in your id generator

Example fix

// before
let id = SESSION_BOOT_OWNERS_STEM; // collides
// after
let id = format!("session-{}", SESSION_BOOT_OWNERS_STEM);
Defensive patterns

Strategy: validation

Validate before calling

if id.trim() == SESSION_BOOT_OWNERS_STEM { return Err(anyhow!("reserved session id")); }

Type guard

fn is_reserved_id(id: &str) -> bool { id.trim() == SESSION_BOOT_OWNERS_STEM }

Prevention

When it happens

Trigger: Passing a session id whose trimmed value exactly equals the reserved stem (e.g. a name matching the boot owners registry file name).

Common situations: Programmatically generating ids from generic words that happen to match the reserved stem; tests reusing a reserved name.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/069d72c4b2edf81f. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/session_manager.rs:1232

    fn validated_session_id<'a>(&self, id: &'a str) -> std::io::Result<&'a str> {
        let trimmed = id.trim();
        if trimmed.is_empty() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "Session id cannot be empty",
            ));
        }
        if !trimmed
            .chars()
            .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
        {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!("Invalid session id '{id}'"),
            ));
        }
        if trimmed == SESSION_BOOT_OWNERS_STEM {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!("Session id '{trimmed}' collides with a reserved sessions file"),
            ));
        }
        Ok(trimmed)
    }

    fn validated_session_path(&self, id: &str) -> std::io::Result<PathBuf> {
        let trimmed = self.validated_session_id(id)?;
        Ok(self.sessions_dir.join(format!("{trimmed}.json")))
    }

    fn checkpoints_dir(&self) -> PathBuf {
        self.sessions_dir.join("checkpoints")
    }

    fn session_goals_dir(&self) -> PathBuf {
        self.sessions_dir.join(SESSION_GOALS_DIR)

View on GitHub (pinned to 433685b202)