{"record":{"id":"ab05a12dc6e5fdbc","repo":"tinyhumansai/openhuman","slug":"refusing-to-follow-symlink","errorCode":null,"errorMessage":"refusing to follow symlink","messagePattern":"refusing to follow symlink","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/prompts/agents_md.rs","lineNumber":211,"sourceCode":"/// fstat check afterwards). This closes the check-to-open race that a\n/// stat-then-`File::open` sequence would leave open. See [`load_agents_md`].\n#[cfg(unix)]\nfn open_no_follow(path: &Path) -> std::io::Result<std::fs::File> {\n    use std::os::unix::fs::OpenOptionsExt;\n    std::fs::OpenOptions::new()\n        .read(true)\n        .custom_flags(libc::O_NOFOLLOW | libc::O_NONBLOCK)\n        .open(path)\n}\n\n/// Non-Unix fallback: best-effort pre-open symlink check. Windows symlink\n/// creation requires elevation / developer mode, so the residual\n/// check-to-open race is low risk on these platforms.\n#[cfg(not(unix))]\nfn open_no_follow(path: &Path) -> std::io::Result<std::fs::File> {\n    let meta = std::fs::symlink_metadata(path)?;\n    if meta.file_type().is_symlink() {\n        return Err(std::io::Error::new(\n            std::io::ErrorKind::InvalidInput,\n            \"refusing to follow symlink\",\n        ));\n    }\n    std::fs::File::open(path)\n}\n\n/// Whether an [`open_no_follow`] error is the \"refused a symlink\" signal (as\n/// opposed to a genuine I/O failure), so the caller can log it distinctly.\n#[cfg(unix)]\nfn is_symlink_refusal(e: &std::io::Error) -> bool {\n    // `O_NOFOLLOW` on a symlink yields `ELOOP` on Linux/macOS and `EMLINK` on\n    // some BSDs — either way the open was refused *because* it was a symlink.\n    matches!(e.raw_os_error(), Some(v) if v == libc::ELOOP || v == libc::EMLINK)\n}\n\n#[cfg(not(unix))]\nfn is_symlink_refusal(e: &std::io::Error) -> bool {","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/agent/prompts/agents_md.rs#L193-L229","documentation":"The AGENTS.md loader opened the file with O_NOFOLLOW and the kernel rejected it because the path is a symlink. This is the load-time half of the prompt-injection hardening: a symlinked AGENTS.md could redirect agent instructions outside the workspace, so the open itself is refused rather than the file being read and checked afterwards. On Unix the check is atomic (open-time flag); the message comes from the io::Error returned by OpenOptions::open when ELOOP occurs.","triggerScenarios":"Thrown at src/openhuman/agent/prompts/agents_md.rs:211 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Inspect the path with ls -la / readlink to confirm it is a symlink and where it points","Replace the symlink with a real file (cp the target over the link) so the loader will read it","If the symlink is intentional and trusted, remove it and configure the content directly in the workspace's AGENTS.md"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}