{"record":{"id":"ac3d92b80c235e0c","repo":"Hmbown/CodeWhale","slug":"could-not-be-read-as-setup-state","errorCode":null,"errorMessage":"{} could not be read as setup state","messagePattern":"(.+?) could not be read as setup state","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/telemetry_notice.rs","lineNumber":371,"sourceCode":"    let mut store = codewhale_config::ConfigStore::load(config_path)?;\n    store\n        .config\n        .set_value(\"telemetry\", if enabled { \"true\" } else { \"false\" })?;\n    store.save()\n}\n\n/// Load a missing sidecar as a fresh state, but distinguish it from an\n/// existing unreadable/corrupt sidecar so the notice can never overwrite the\n/// latter with defaults.\nfn load_notice_state_at(path: &Path) -> Result<SetupState> {\n    if !path\n        .try_exists()\n        .map_err(|error| anyhow!(\"could not inspect {}: {error}\", path.display()))?\n    {\n        return Ok(SetupState::default());\n    }\n    SetupState::load_from(path)\n        .ok_or_else(|| anyhow!(\"{} could not be read as setup state\", path.display()))\n}\n\n/// Everything that decides whether the disclosure may be shown.\nstruct NoticeGate {\n    needs_notice: bool,\n    persisted_off: bool,\n    recorded_opt_out: bool,\n    floor_in_force: bool,\n}\n\nimpl NoticeGate {\n    fn may_ask(&self) -> bool {\n        self.needs_notice && !self.persisted_off && !self.recorded_opt_out && !self.floor_in_force\n    }\n}\n\n#[cfg(test)]\nmod tests {","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/telemetry_notice.rs#L353-L389","documentation":"After confirming the sidecar path exists, `load_notice_state_at` calls `SetupState::load_from(path)`; if that returns None (unreadable, corrupt, or wrong-format sidecar) it throws `\"{} could not be read as setup state\"`. The purpose is protective: an existing but corrupt sidecar must never be replaced by defaults, so the notice gate fails loudly instead.","triggerScenarios":"Calling `plan_for_store_and_state` or `apply_persistent_preference_at` when the sidecar exists but its contents fail `SetupState` parsing — truncated write, hand-edited file, empty file, wrong schema version, or the path points at a directory or binary garbage.","commonSituations":"A crash or power loss during a previous write left a partial sidecar; the user edited the setup-state file manually; a version upgrade changed the on-disk format; the path was repointed at a different non-state file.","solutions":["Back up the sidecar file, then delete or rename it so a fresh valid state can be written on next run.","Inspect the file contents and repair it to the expected setup-state format (or restore from a backup taken before an upgrade).","Check logs for the prior write that may have been interrupted; ensure the process is not killed mid-write (clean shutdown).","If corrupt sidecars recur, report it — repeated truncation suggests a locking/fsync bug in the persistence path."],"exampleFix":"// before\nmv ~/.config/codewhale/telemetry-notice.json ~/.config/codewhale/telemetry-notice.json.bak\n// after\n# fresh default state is created on next launch\nrm ~/.config/codewhale/telemetry-notice.json.bak","handlingStrategy":"fallback","validationCode":"// Rust-style caller pre-check\nif path.exists() {\n    let raw = std::fs::read(&path)?;\n    anyhow::ensure!(!raw.is_empty(), \"sidecar {} is empty/corrupt\", path.display());\n    // optionally attempt a parse here before invoking the library\n}","typeGuard":"fn sidecar_parseable(path: &std::path::Path) -> bool {\n    std::fs::read(path).map(|raw| !raw.is_empty()).unwrap_or(false)\n}","tryCatchPattern":"match load_notice_state_at(&path) {\n    Ok(state) => state,\n    Err(e) if e.to_string().contains(\"could not be read as setup state\") => {\n        // corrupt sidecar: back it up, then fall back to fresh defaults\n        let _ = std::fs::rename(&path, path.with_extension(\"json.corrupt\"));\n        SetupState::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Do not hand-edit setup-state sidecar files.","Ensure clean shutdown so writes are never truncated mid-flight.","After version upgrades, verify sidecar format compatibility or delete stale sidecars.","Keep a backup of the config directory before upgrades."],"tags":["filesystem","corrupt-data","parsing"],"backgroundTag":"file-read-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}