Hmbown/CodeWhale · error · anyhow::Error

failed to open

Error message

failed to open {}

What it means

The telemetry buffer failed to open the tombstone file at its expected path. NotFound is treated as a normal 'no tombstone' outcome (returns Ok(None)); any other open error (permissions, is-a-directory, I/O failure) is wrapped in anyhow with the path in the context message during tombstone reading (called by wipe and arm).

Solutions

  1. Check permissions on the tombstone file and its parent directory (`ls -la` the path printed in the message) and chown/chmod to the running user.
  2. If a directory occupies the tombstone path, remove it so the file can be recreated.
  3. Verify the filesystem is writable and not remounted read-only (`mount | grep <path>`).
  4. As a last resort, delete the corrupted tombstone file; NotFound is handled gracefully and state will be rebuilt.

Example fix

// before
sudo ./codewhale  # creates telemetry files owned by root

// after
sudo chown -R $USER ~/.local/share/codewhale/telemetry
Defensive patterns

Strategy: try-catch

Validate before calling

let p = telemetry::tombstone_path(root);
let meta = std::fs::metadata(&p);
let precheck_ok = match &meta {
    Ok(m) => m.is_file() && !m.permissions().readonly(),
    Err(e) => e.kind() == std::io::ErrorKind::NotFound,
};

Try / catch

match arm_or_wipe() {
    Ok(v) => v,
    Err(e) if e.to_string().contains("failed to open") => {
        // log path from message, fix perms or delete stale tombstone, retry once
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `wipe` or `arm` when the tombstone file exists at `tombstone_path(root)` but cannot be opened: permission denied, path is a directory, stale symlink, or underlying I/O error.

Common situations: Telemetry data directory owned by a different user after running as root once; a directory accidentally created where the tombstone file should be; NFS/overlay filesystem returning EIO; read-only mount after crash recovery.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/d0c5d39276f16290. Report an issue: GitHub.

Appendix: source

Thrown at crates/telemetry/src/buffer.rs:114

///
/// Re-checked on **every** append and immediately before **every** send. This is
/// what makes `codewhale config set telemetry false` — an external write by
/// another process — observable to a session that is already running.
#[must_use]
pub fn tombstone_present(root: &Path) -> bool {
    tombstone_path(root).exists()
}

/// Read the exact tombstone generation, or `None` when collection has never
/// been disabled in this home.
pub(crate) fn tombstone_generation(root: &Path) -> Result<Option<TombstoneGeneration>> {
    let path = tombstone_path(root);
    let file = match File::open(&path) {
        Ok(file) => file,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
        Err(error) => {
            return Err(
                anyhow::Error::new(error).context(format!("failed to open {}", path.display()))
            );
        }
    };
    let mut bytes = Vec::new();
    file.take(MAX_TOMBSTONE_BYTES + 1)
        .read_to_end(&mut bytes)
        .with_context(|| format!("failed to read {}", path.display()))?;
    if bytes.len() as u64 > MAX_TOMBSTONE_BYTES {
        anyhow::bail!("{} exceeds the tombstone size limit", path.display());
    }
    Ok(Some(TombstoneGeneration(bytes)))
}

/// Create the telemetry directory `0700`, if it is missing.
pub fn ensure_dir(root: &Path) -> Result<()> {
    if root.is_dir() {
        return Ok(());
    }

View on GitHub (pinned to 73e0f67d83)