Hmbown/CodeWhale · error · anyhow::Error

{} has multiple filesystem links, not a unique workspace-own

Error message

{} has multiple filesystem links, not a unique workspace-owned file

What it means

After the regular-file check, workspace_dotenv_has_multiple_links verifies the opened file's link count is exactly 1. A hard link (st_nlink > 1) means another path elsewhere on the same filesystem can mutate the credentials the workspace reads, so the load is refused to keep the credential file uniquely workspace-owned — the hardlink sibling of the symlink rule.

Source

Thrown at crates/tui/src/lib.rs:2625

            continue;
        }
        if ch == '$' {
            return true;
        }
    }
    false
}

fn read_stable_workspace_dotenv(path: &Path) -> Result<Vec<u8>> {
    let mut file = open_workspace_dotenv_without_following_links(path)?;
    let metadata = file
        .metadata()
        .map_err(|error| anyhow!("could not inspect {}: {error}", path.display()))?;
    if !metadata.is_file() {
        bail!("{} is not a regular file", path.display());
    }
    if workspace_dotenv_has_multiple_links(&file, &metadata)? {
        bail!(
            "{} has multiple filesystem links, not a unique workspace-owned file",
            path.display()
        );
    }
    if metadata.len() > MAX_WORKSPACE_DOTENV_BYTES {
        bail!(
            "{} exceeds the {} byte workspace .env limit",
            path.display(),
            MAX_WORKSPACE_DOTENV_BYTES
        );
    }

    let mut contents = Vec::with_capacity(metadata.len() as usize);
    (&mut file)
        .take(MAX_WORKSPACE_DOTENV_BYTES + 1)
        .read_to_end(&mut contents)
        .map_err(|error| anyhow!("could not read {}: {error}", path.display()))?;
    if contents.len() as u64 > MAX_WORKSPACE_DOTENV_BYTES {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Break the link: copy the content to a fresh file (`cp .env /tmp/x && rm .env && cp /tmp/x .env` or rewrite in place)
  2. Stop hardlink-based cache/dedup tooling from managing workspace .env files
  3. Give each checkout its own literal .env instead of sharing via links

Example fix

# before
stat -c '%h %n' .env   # 3 .env   (three hard links)

# after
cp .env .env.tmp && rm .env && mv .env.tmp .env
stat -c '%h %n' .env   # 1 .env
Defensive patterns

Strategy: validation

Validate before calling

# Link count must be 1
[ "$(stat -c '%h' .env 2>/dev/null || stat -f '%l' .env)" = '1' ] || {
  echo '.env has multiple hard links'; exit 2
}

Prevention

When it happens

Trigger: Backup/sync tooling that hard-links .env (ln, rsync --link-dest, snapshot/cache managers); deliberately sharing one credential file between multiple checkouts via hard links; a hard link created between open and the link-count check.

Common situations: Build caches and dedup tools hardlinking dotfiles; monorepo scripts sharing one .env across worktrees; disk-space-saving tricks that backfire on credential files.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/b32fe90608a2164e. Report an issue: GitHub.