Hmbown/CodeWhale · error
Task record identity differs from its path
Error message
Task record identity differs from its path
What it means
Each task file must be named <task_id>.json, and the record inside must carry that same id. After validating the id is a safe storage id, the manager compares the file stem to task.id; a mismatch means the durable identity and the record disagree, which would corrupt id-based lookups, so it fails.
Solutions
- Rename the file so its stem equals the record's `task.id` (or vice versa: edit record.id to match the filename)
- Regenerate the task properly instead of copying files manually
- Re-run recovery after fixing, and avoid hand-editing task store files
Example fix
// before: file 9f2e....json contains id task_00ff... cp task_aaaa000000000000.json task_bbbb000000000000.json // after: also update the inner id sed -i 's/task_aaaa000000000000/task_bbbb000000000000/' task_bbbb000000000000.json
Defensive patterns
Strategy: validation
Validate before calling
let stem = path.file_stem().and_then(|s| s.to_str()).unwrap_or_default();
if stem != record.id { return Err(anyhow!("filename {stem} does not match record id {}", record.id)); } Type guard
fn file_matches_record(path: &Path, record: &TaskRecord) -> bool {
path.file_stem().and_then(|s| s.to_str()) == Some(record.id.as_str())
} Try / catch
match manager.load_task_file(&path).await {
Err(e) if e.to_string().contains("identity differs from its path") => {
eprintln!("repairing: rename file to match record id");
std::fs::rename(&path, dir.join(format!("{}.json", record.id)))?;
manager.load_task_file(&path).await?;
}
other => other?,
} Prevention
- Never rename or copy task files manually; use the manager API
- When seeding tasks by copy, update the inner id to the new filename
- Audit stores after sync/restore operations for file/content identity mismatches
When it happens
Trigger: Loading a task file whose filename stem (e.g. other_id from other_id.json) does not equal the decoded record's `task.id` — typically after a manual rename, copy, or hand-edited record.
Common situations: Manually renaming a .json task file; copying a task file to seed a new task without editing the inner id; a restore/sync tool that renamed files but not contents; a merge of two stores.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Bound task identity or schema does not match its durable…
- Failed to promote staged task
- Task id already exists
- Unaccepted task stage changed during recovery
- advisor model ` ` belongs to a custom provider but no exact…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/321d2d120ed8483b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/task_manager.rs:3170
let entry = entry?;
let path = entry.path();
if path.extension().is_none_or(|ext| ext != "json") {
continue;
}
let content = fs::read_to_string(&path)
.with_context(|| format!("Failed to read task file {}", path.display()))?;
let task: TaskRecord = serde_json::from_str(&content)
.with_context(|| format!("Failed to parse task file {}", path.display()))?;
if task.schema_version > CURRENT_TASK_SCHEMA_VERSION {
bail!(
"Task schema v{} is newer than supported v{}",
task.schema_version,
CURRENT_TASK_SCHEMA_VERSION
);
}
ensure_safe_storage_id("task id", &task.id)?;
if path.file_stem().and_then(|stem| stem.to_str()) != Some(task.id.as_str()) {
bail!("Task record identity differs from its path");
}
if let Some(scope) = &task.execution_scope {
validate_execution_id(scope, 64)?;
}
if let Some(generation) = &task.execution_generation {
validate_execution_id(generation, 32)?;
if task.execution_scope.is_none() {
bail!("Task generation has no execution scope");
}
}
tasks.insert(task.id.clone(), task);
}
}
let mut queue = if queue_path.exists() {
let content = fs::read_to_string(queue_path)
.with_context(|| format!("Failed to read queue file {}", queue_path.display()))?;
let parsed: QueueFile = serde_json::from_str(&content)View on GitHub (pinned to 73e0f67d83)