Hmbown/CodeWhale · error

could not inspect

Error message

could not inspect {}: {error}

What it means

`load_notice_state_at` must distinguish 'no sidecar file' (fresh default state) from 'cannot even stat the path' (environment failure). It calls `path.try_exists()` and wraps any I/O error from that probe in `anyhow!("could not inspect {}: {error}")`. This deliberately keeps the notice gate from silently treating a broken filesystem path as 'fresh install' and overwriting real state with defaults.

Solutions

  1. Check the OS error in the message: fix permissions on the path and its parent directory (chmod/chown or run as the owning user).
  2. Recreate the missing parent directory that the sidecar path expects (e.g. mkdir -p on the config directory).
  3. Verify the storage medium is mounted and healthy; remount network/external drives before launching.
  4. As a last resort, point the sidecar location (config/env) at a writable local path.

Example fix

// before
// HOME=/nonexistent ; codewhale (stat fails)
// after
export HOME=/home/realuser
mkdir -p "$HOME/.config/codewhale"
codewhale
Defensive patterns

Strategy: validation

Validate before calling

// Rust-style pre-check by the caller/environment
let dir = sidecar_path.parent().expect("sidecar path has a parent");
anyhow::ensure!(dir.exists(), "config dir {} is missing", dir.display());
let meta = std::fs::metadata(&sidecar_path)
    .map_err(|e| anyhow!("sidecar path not inspectable: {e}"))?;
let _ = meta; // stat succeeded; safe to proceed

Type guard

fn sidecar_inspectable(path: &std::path::Path) -> bool {
    std::fs::metadata(path).is_ok()
}

Try / catch

match load_notice_state_at(&path) {
    Ok(state) => state,
    Err(e) if e.to_string().contains("could not inspect") => {
        // filesystem-level failure: surface to user, never overwrite state
        return Err(e.context("cannot access telemetry sidecar path"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `plan_for_store_and_state` or `apply_persistent_preference_at` when the sidecar path's parent directory is missing/unreadable, permissions deny stat access, the path crosses a broken mount/network drive, or an OS-level error (e.g. ELOOP, EIO) occurs during the existence probe.

Common situations: Running Codewhale under a service account whose HOME points at a nonexistent directory; a read-only or corrupted config volume; Windows/OneDrive or NFS sync placeholders making stat fail; symlink loops in the config path.

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/facd67852c7508ff. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/telemetry_notice.rs:366

        bounded
    }
}

fn write_config_preference(config_path: Option<PathBuf>, enabled: bool) -> Result<()> {
    let mut store = codewhale_config::ConfigStore::load(config_path)?;
    store
        .config
        .set_value("telemetry", if enabled { "true" } else { "false" })?;
    store.save()
}

/// Load a missing sidecar as a fresh state, but distinguish it from an
/// existing unreadable/corrupt sidecar so the notice can never overwrite the
/// latter with defaults.
fn load_notice_state_at(path: &Path) -> Result<SetupState> {
    if !path
        .try_exists()
        .map_err(|error| anyhow!("could not inspect {}: {error}", path.display()))?
    {
        return Ok(SetupState::default());
    }
    SetupState::load_from(path)
        .ok_or_else(|| anyhow!("{} could not be read as setup state", path.display()))
}

/// Everything that decides whether the disclosure may be shown.
struct NoticeGate {
    needs_notice: bool,
    persisted_off: bool,
    recorded_opt_out: bool,
    floor_in_force: bool,
}

impl NoticeGate {
    fn may_ask(&self) -> bool {
        self.needs_notice && !self.persisted_off && !self.recorded_opt_out && !self.floor_in_force

View on GitHub (pinned to 73e0f67d83)