{"record":{"id":"7992441cba790a83","repo":"Hmbown/CodeWhale","slug":"sub-agent-state-path-must-be-a-regular-file","errorCode":null,"errorMessage":"sub-agent state path must be a regular file: {}","messagePattern":"sub-agent state path must be a regular file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":7762,"sourceCode":"            continue;\n        };\n        if metadata.file_type().is_symlink() {\n            return Err(anyhow!(\n                \"sub-agent state path must not traverse symlinks: {}\",\n                current.display()\n            ));\n        }\n    }\n    Ok(())\n}\n\nfn read_subagent_state_file(state_root: &Path, path: &Path) -> Result<String> {\n    let state_root = normalize_subagent_workspace(state_root);\n    reject_root_relative_symlinks(&state_root, path)?;\n    let metadata = fs::symlink_metadata(path)?;\n    let file_type = metadata.file_type();\n    if file_type.is_symlink() || !file_type.is_file() {\n        return Err(anyhow!(\n            \"sub-agent state path must be a regular file: {}\",\n            path.display()\n        ));\n    }\n\n    let mut file = open_subagent_state_file(path)?;\n    let mut raw = String::new();\n    file.read_to_string(&mut raw)?;\n    Ok(raw)\n}\n\n#[cfg(unix)]\nfn open_subagent_state_file(path: &Path) -> Result<fs::File> {\n    use std::os::unix::fs::OpenOptionsExt;\n\n    fs::OpenOptions::new()\n        .read(true)\n        .custom_flags(libc::O_NOFOLLOW)","sourceCodeStart":7744,"sourceCodeEnd":7780,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/subagent/mod.rs#L7744-L7780","documentation":"`read_subagent_state_file` lstats the path and requires a regular, non-symlink file before opening; reading through symlinks, fifos, sockets, or directories is refused. This pairs with the symlink-traversal guard so state reads cannot be redirected or blocked on special files.","triggerScenarios":"The state path is a symlink to a file elsewhere, a named pipe a process is holding, or a directory at the expected file location.","commonSituations":"Symlinked state files (manual shortcuts, tooling); a hung writer leaving a fifo; path collisions where a directory was created at the file's path.","solutions":["Inspect the path with ls -l; remove any symlink or special file and restore a regular file (or let the manager recreate it).","Never link state files — keep them as regular files inside the state root.","If a directory occupies the path, move it and retry."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(&path)?;\nif meta.file_type().is_symlink() || !meta.file_type().is_file() {\n    // not readable as state: replace with a regular file before calling\n}","typeGuard":"fn is_regular_non_symlink(meta: &std::fs::Metadata) -> bool {\n    !meta.file_type().is_symlink() && meta.file_type().is_file()\n}","tryCatchPattern":"match read_subagent_state_file(&state_root, &path) {\n    Err(e) if e.to_string().contains(\"must be a regular file\") => { /* remove symlink/fifo, restore file, retry */ }\n    r => r?,\n}","preventionTips":["Never link state files; keep them regular files in the state root.","Audit for path collisions where a directory replaces an expected file."],"tags":["subagent","symlink","regular-file","security","rust"],"backgroundTag":"symlink-rejected","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}