Hmbown/CodeWhale · error
could not securely open
Error message
could not securely open {}: {error} What it means
The Unix open path for the workspace `.env` uses `O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK` so symlinks are never followed and a FIFO named `.env` cannot hang startup. If `open` fails (including the ELOOP that O_NOFOLLOW produces for symlinks) it is wrapped in this error.
Solutions
- Replace the `.env` symlink with a real regular file copy inside the workspace.
- Fix file permissions so the running user can read it.
- Create the file if it is missing, or unset the expectation of a workspace `.env` for this project.
Example fix
// before (shell) ln -s ~/secrets/.env .env // after cp ~/secrets/.env .env && chmod 600 .env
Defensive patterns
Strategy: validation
Validate before calling
let md = std::fs::symlink_metadata(".env")?;
if md.file_type().is_symlink() {
eprintln!(".env must be a regular file, not a symlink");
}
if !md.is_file() { eprintln!(".env is not a regular file"); } Type guard
fn is_safe_dotenv(path: &Path) -> bool {
std::fs::symlink_metadata(path)
.map(|md| md.is_file())
.unwrap_or(false)
} Try / catch
match load_workspace_dotenv_credentials_from_path(&path) {
Err(e) if e.to_string().contains("could not securely open") => {
eprintln!(".env must be a readable regular file (no symlinks): {e:#}");
}
other => other?,
} Prevention
- Copy shared dotfiles into the workspace instead of symlinking `.env`.
- Set restrictive permissions (`chmod 600 .env`) and correct ownership.
- Verify with `ls -la .env` that it is a regular file before launching.
When it happens
Trigger: `OpenOptions::open(path)` returns an io::Error: `ELOOP` (path is a symlink, rejected by O_NOFOLLOW), `ENOENT` (missing file), `EACCES` (permissions), or a non-regular file type.
Common situations: A `.env` that is actually a symlink (common when people symlink shared dotfiles), or one unreadable due to permissions/ownership.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- is a symbolic link, not a workspace-owned file
- built-in plugin path may not be a symbolic link or reparse…
- CodewhalePet/1
- could not inspect workspace .env link count
- external credential path must name a non-reparse regular…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/99f6438dfce79dac.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/lib.rs:3023
#[cfg(not(any(unix, windows)))]
fn workspace_dotenv_has_multiple_links(
_file: &std::fs::File,
_metadata: &std::fs::Metadata,
) -> Result<bool> {
Ok(false)
}
#[cfg(unix)]
fn open_workspace_dotenv_without_following_links(path: &Path) -> Result<std::fs::File> {
use std::os::unix::fs::OpenOptionsExt;
std::fs::OpenOptions::new()
.read(true)
// `O_NONBLOCK` is inert for regular files but prevents a FIFO named
// `.env` from hanging startup before the metadata check can reject it.
.custom_flags(libc::O_CLOEXEC | libc::O_NOFOLLOW | libc::O_NONBLOCK)
.open(path)
.map_err(|error| anyhow!("could not securely open {}: {error}", path.display()))
}
#[cfg(windows)]
fn open_workspace_dotenv_without_following_links(path: &Path) -> Result<std::fs::File> {
use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
let file = std::fs::OpenOptions::new()
.read(true)
.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT)
.open(path)
.map_err(|error| anyhow!("could not securely open {}: {error}", path.display()))?;
let metadata = file
.metadata()
.map_err(|error| anyhow!("could not inspect {}: {error}", path.display()))?;
if metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
bail!(View on GitHub (pinned to 73e0f67d83)