{"record":{"id":"b5a34e82248e57cd","repo":"Hmbown/CodeWhale","slug":"sub-agent-transcript-artifact-is-not-a-regular-fil","errorCode":null,"errorMessage":"sub-agent transcript artifact is not a regular file: {}","messagePattern":"sub-agent transcript artifact is not a regular file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":7638,"sourceCode":"                .cloned()\n                .ok_or_else(|| anyhow!(\"sub-agent transcript record is missing its message\"))?,\n        )?;\n        messages.push(message);\n    }\n    Ok(messages)\n}\n\nfn remove_subagent_transcript_artifact(state_root: &Path, agent_id: &str) -> Result<bool> {\n    let state_root = normalize_subagent_workspace(state_root);\n    let path = checked_subagent_transcript_artifact_path(&state_root, agent_id)?;\n    reject_root_relative_symlinks(&state_root, &path)?;\n    let metadata = match fs::symlink_metadata(&path) {\n        Ok(metadata) => metadata,\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false),\n        Err(err) => return Err(err.into()),\n    };\n    if metadata.file_type().is_symlink() || !metadata.file_type().is_file() {\n        return Err(anyhow!(\n            \"sub-agent transcript artifact is not a regular file: {}\",\n            path.display()\n        ));\n    }\n    fs::remove_file(path)?;\n    Ok(true)\n}\n\n#[cfg(test)]\npub(crate) fn write_subagent_transcript_artifact_for_test(\n    state_root: &Path,\n    agent_id: &str,\n    messages: &[Message],\n) -> Result<PathBuf> {\n    let mut writer = SubAgentTranscriptArtifactWriter::create(state_root, agent_id)?;\n    writer.sync_messages(messages, true)?;\n    Ok(writer.path)\n}","sourceCodeStart":7620,"sourceCodeEnd":7656,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/subagent/mod.rs#L7620-L7656","documentation":"`remove_subagent_transcript_artifact` lstats the artifact path via symlink_metadata and refuses to unlink when it is a symlink or not a regular file. This prevents cleanup from following a link out of the sub-agent state root or deleting special files, so a tampered path fails closed instead of erasing something else.","triggerScenarios":"The artifact path was replaced by a symlink (accidental or hostile), or the file became a fifo/socket/directory while the manager held the record.","commonSituations":"State dir manipulated by other tooling or a compromised child; users \"organizing\" state with symlinks; container mounts overlaying state paths.","solutions":["Inspect the path with ls -l / readlink; if the symlink is intentional and safe, remove it manually with rm on the link itself.","Restore a regular file (or let the manager recreate it) and retry cleanup.","Keep the state root free of symlinks and external mounts."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(&artifact_path)?;\nlet ok = !meta.file_type().is_symlink() && meta.file_type().is_file();\nif !ok { /* do not call remove; handle the symlink/special file manually */ }","typeGuard":"fn is_removable_regular_file(meta: &std::fs::Metadata) -> bool {\n    !meta.file_type().is_symlink() && meta.file_type().is_file()\n}","tryCatchPattern":"match remove_subagent_transcript_artifact(&state_root, agent_id) {\n    Err(e) if e.to_string().contains(\"not a regular file\") => { /* inspect with readlink, remove link manually, log */ }\n    r => r,\n}","preventionTips":["Keep the state root free of symlinks and special files.","Monitor for child processes creating links inside state dirs."],"tags":["subagent","transcript","symlink","cleanup","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"}