Hmbown/CodeWhale · error · anyhow::Error
sub-agent state path must be a regular file
Error message
sub-agent state path must be a regular file: {} What it means
When reading a sub-agent state file, the library takes `fs::symlink_metadata` and requires the entry to be a regular file; symlinks, directories, and special files are rejected even if reading through them would succeed. This guarantees state contents come from the real file inside the state root, not an alias.
Solutions
- Replace the symlink/directory with a regular file containing the state (copy the target's contents).
- Delete the unexpected entry and let the library recreate the state file.
- Configure dotfile managers to copy rather than symlink files under the state root.
- Verify container/volume setups so the state path is a plain file.
Example fix
// before: state file is a symlink state/session.json -> /mnt/shared/session.json // after rm state/session.json cp /mnt/shared/session.json state/session.json
Defensive patterns
Strategy: validation
Validate before calling
use std::fs;
fn readable_regular_state_file(p: &std::path::Path) -> bool {
fs::symlink_metadata(p).map(|m| m.file_type().is_file()).unwrap_or(false)
}
// guard before reading: readable_regular_state_file(&state_path) Try / catch
match read_state(root, path) {
Ok(contents) => contents,
Err(e) if e.to_string().contains("must be a regular file") => {
// entry is a symlink/dir: restore a real file or accept defaults
Default::default()
}
Err(e) => return Err(e),
} Prevention
- Never symlink individual state files (dotfile managers: use copy mode).
- Verify a directory was not created where the state file belongs.
- Restore state files as regular files after backup/restore operations.
When it happens
Trigger: Calling a sub-agent state read API where the target path is a symlink, a directory, or a special file — `file_type().is_symlink() || !file_type().is_file()`.
Common situations: The state file was replaced by a symlink to a shared/dotfile-managed location; a directory was created at the expected file path; a FIFO or device node occupies the path in a container.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- sub-agent transcript artifact is not a regular file
- Checkpoint continuation requires a source agent
- CodewhalePet/1
- Custom sub-agent requires a non-empty allowed_tools list
- deliverable is outside the worker write scope; declare an…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/7992441cba790a83.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:8918
"sub-agent state path must not traverse symlinks: {}",
current.display()
));
}
}
Ok(())
}
/// Maximum bytes read from a sub-agent state or transcript artifact file.
/// Generous for long session histories; anything larger is corrupt.
const MAX_SUBAGENT_STATE_BYTES: u64 = 16 * 1024 * 1024;
fn read_subagent_state_file(state_root: &Path, path: &Path) -> Result<String> {
let state_root = normalize_subagent_workspace(state_root);
reject_root_relative_symlinks(&state_root, path)?;
let metadata = fs::symlink_metadata(path)?;
let file_type = metadata.file_type();
if file_type.is_symlink() || !file_type.is_file() {
return Err(anyhow!(
"sub-agent state path must be a regular file: {}",
path.display()
));
}
let file = open_subagent_state_file(path)?;
let mut raw = String::new();
file.take(MAX_SUBAGENT_STATE_BYTES + 1)
.read_to_string(&mut raw)?;
if raw.len() as u64 > MAX_SUBAGENT_STATE_BYTES {
return Err(anyhow!(
"sub-agent state file {} exceeds the 16 MiB limit",
path.display()
));
}
Ok(raw)
}
View on GitHub (pinned to 73e0f67d83)