{"record":{"id":"99f6438dfce79dac","repo":"Hmbown/CodeWhale","slug":"could-not-securely-open-error","errorCode":null,"errorMessage":"could not securely open {}: {error}","messagePattern":"could not securely open (.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/lib.rs","lineNumber":2671,"sourceCode":"#[cfg(not(any(unix, windows)))]\nfn workspace_dotenv_has_multiple_links(\n    _file: &std::fs::File,\n    _metadata: &std::fs::Metadata,\n) -> Result<bool> {\n    Ok(false)\n}\n\n#[cfg(unix)]\nfn open_workspace_dotenv_without_following_links(path: &Path) -> Result<std::fs::File> {\n    use std::os::unix::fs::OpenOptionsExt;\n\n    std::fs::OpenOptions::new()\n        .read(true)\n        // `O_NONBLOCK` is inert for regular files but prevents a FIFO named\n        // `.env` from hanging startup before the metadata check can reject it.\n        .custom_flags(libc::O_CLOEXEC | libc::O_NOFOLLOW | libc::O_NONBLOCK)\n        .open(path)\n        .map_err(|error| anyhow!(\"could not securely open {}: {error}\", path.display()))\n}\n\n#[cfg(windows)]\nfn open_workspace_dotenv_without_following_links(path: &Path) -> Result<std::fs::File> {\n    use std::os::windows::fs::{MetadataExt, OpenOptionsExt};\n\n    const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;\n    const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;\n    let file = std::fs::OpenOptions::new()\n        .read(true)\n        .custom_flags(FILE_FLAG_OPEN_REPARSE_POINT)\n        .open(path)\n        .map_err(|error| anyhow!(\"could not securely open {}: {error}\", path.display()))?;\n    let metadata = file\n        .metadata()\n        .map_err(|error| anyhow!(\"could not inspect {}: {error}\", path.display()))?;\n    if metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0 {\n        bail!(","sourceCodeStart":2653,"sourceCodeEnd":2689,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/lib.rs#L2653-L2689","documentation":"On Unix, Codewhale opens .env with O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC. O_NOFOLLOW makes the open fail with ELOOP if the final path component is a symbolic link; O_NONBLOCK stops a FIFO named .env from hanging startup before the regular-file check can reject it. Any open(2) failure surfaces as 'could not securely open {path}'.","triggerScenarios":"A .env that is a symlink (ELOOP, typically reported as 'Too many levels of symbolic links'); EACCES on the file or a parent directory; the file vanishing between the ancestor walk and the open (ENOENT race); opening certain device special files.","commonSituations":"A developer symlinks .env to a shared secret outside the repo (ln -s ~/.secrets/env .env) — this is rejected on purpose; group or world-unreadable permissions on .env; secret managers that install links instead of copying files.","solutions":["Replace the symlink with a real file: copy the target's contents into .env and set restrictive permissions","Have your secret manager write or copy the actual file into the workspace at deploy time instead of linking","Fix permissions: chmod 600 .env and ensure every parent directory is traversable","If indirection is required, link the parent directory, not the .env itself — O_NOFOLLOW only guards the final component"],"exampleFix":"# before\nln -s ~/.secrets/shared.env .env\n\n# after\ncp ~/.secrets/shared.env .env && chmod 600 .env","handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(\".env\")?;\nif meta.file_type().is_symlink() {\n    anyhow::bail!(\".env is a symlink; Codewhale rejects it — copy a real file in\");\n}\nif !meta.is_file() {\n    anyhow::bail!(\".env is not a regular file\");\n}","typeGuard":"fn is_plain_regular_file(path: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|meta| meta.is_file())\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Never symlink .env; store a real file per workspace","chmod 600 .env and keep every parent directory traversable","Re-check after secret managers run — some install links instead of copying files"],"tags":["rust","unix","symlink","env","security","dotenv"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}