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
- Check the OS error in the message: fix permissions on the path and its parent directory (chmod/chown or run as the owning user).
- Recreate the missing parent directory that the sidecar path expects (e.g. mkdir -p on the config directory).
- Verify the storage medium is mounted and healthy; remount network/external drives before launching.
- 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
- Ensure the config directory exists and is writable by the running user before launching.
- Do not run first-time setup under elevation or a different service account than later runs.
- Keep sidecar storage on a local, mounted volume — not network drives or sync placeholders.
- Check HOME/USERPROFILE environment variables when running under services or containers.
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
- could not inspect
- could not read
- {error}
- external credential path must name a regular file
- failed to open
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_forceView on GitHub (pinned to 73e0f67d83)