Hmbown/CodeWhale · error · io::Error
Session id ' ' collides with a reserved checkpoint file
Error message
Session id '{}' collides with a reserved checkpoint file What it means
checkpoint_path validates the session id and then additionally rejects ids whose '<id>.json' equals LEGACY_CHECKPOINT_FILE or OFFLINE_QUEUE_FILE, with 'Session id ... collides with a reserved checkpoint file'. This prevents a per-session checkpoint from overwriting the legacy checkpoint or offline-queue files that live in the same checkpoints/ directory.
Solutions
- Pick a session id whose .json name differs from LEGACY_CHECKPOINT_FILE and OFFLINE_QUEUE_FILE
- Rename or migrate the foreign/imported session before loading
- Add a suffix/prefix to generated ids (e.g. session-<uuid>) so they cannot equal reserved stems
Example fix
// before let id = "offline-queue"; // collides with OFFLINE_QUEUE_FILE // after let id = "offline-queue-session";
Defensive patterns
Strategy: validation
Validate before calling
let name = format!("{}.json", id.trim());
if name == LEGACY_CHECKPOINT_FILE || name == OFFLINE_QUEUE_FILE { return Err(anyhow!("id collides with reserved checkpoint file")); } Type guard
fn safe_checkpoint_id(id: &str) -> bool {
let t = id.trim();
!t.is_empty()
&& t.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
&& format!("{t}.json") != "checkpoint.json"
&& format!("{t}.json") != "offline-queue.json"
} Try / catch
match manager.checkpoint_path(&id) {
Ok(p) => use(p),
Err(e) if e.to_string().contains("reserved checkpoint file") => eprintln!("Session id '{}' uses a reserved file name; rename it", id),
Err(e) => return Err(e.into()),
} Prevention
- Prefix generated ids (e.g. 'session-') so they can never equal reserved stems
- Check the checkpoints/ directory listing before importing sessions with hand-picked ids
- Keep an exclusion list in import tooling mirroring the reserved-file constants
When it happens
Trigger: Calling checkpoint_path (or APIs that derive checkpoint paths from a session id) with an id like the legacy checkpoint stem or the offline-queue stem — e.g. id 'offline-queue' when OFFLINE_QUEUE_FILE is 'offline-queue.json'.
Common situations: Users naming sessions after the reserved stems seen in the checkpoints directory listing; imported or hand-copied session files using legacy names; automated id generation colliding with the legacy file constants.
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
- Session id ' ' collides with a reserved sessions file
- custom provider name is reserved
- Invalid session id
- Invalid session id
- Session id cannot be empty
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/ebc97006dc3e0770.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/session_manager.rs:1368
Err(error) => return Err(error),
};
if metadata.file_type().is_symlink() || !metadata.is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Session goal {} must be a regular file", path.display()),
));
}
Ok(true)
}
fn validated_checkpoint_path(&self, session_id: &str) -> std::io::Result<PathBuf> {
let trimmed = self.validated_session_id(session_id)?;
// Reserved file names inside `checkpoints/` must never collide with a
// per-session checkpoint file.
if format!("{trimmed}.json") == LEGACY_CHECKPOINT_FILE
|| format!("{trimmed}.json") == OFFLINE_QUEUE_FILE
{
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("Session id '{trimmed}' collides with a reserved checkpoint file"),
));
}
Ok(self.checkpoints_dir().join(format!("{trimmed}.json")))
}
/// Create a new `SessionManager` with the specified sessions directory
pub fn new(sessions_dir: PathBuf) -> std::io::Result<Self> {
let sessions_dir = normalize_managed_dir(sessions_dir)?;
// Ensure the sessions directory exists
fs::create_dir_all(&sessions_dir)?;
Ok(Self {
sessions_dir,
retention_in_progress: AtomicBool::new(false),
})
}
View on GitHub (pinned to 73e0f67d83)