Hmbown/CodeWhale · error
Shared habitat could not restore; its file was kept
Error message
Shared habitat could not restore; its file was kept
What it means
During startup the shared habitat's saved state is loaded into the JS runtime and restored via pet.restoreRecording(saved). If that eval fails for any reason, run_world() maps the whole closure error to this message, indicating the saved state file was intentionally kept (not deleted) so restoration can be retried later.
Solutions
- Inspect the kept habitat save file for schema drift or corruption and repair or remove it (it is preserved precisely so you can decide)
- Downgrade/upgrade Codewhale so the runtime's restoreRecording matches the save's format
- Reproduce with verbose JS error output — the anyhow mapping discards the underlying cause, so check logs for the original eval error
Example fix
// before
.map_err(|_| anyhow::anyhow!("Shared habitat could not restore; its file was kept"))?;
// after
.map_err(|e| anyhow::anyhow!("Shared habitat could not restore; its file was kept: {e}"))?; Defensive patterns
Strategy: validation
Validate before calling
// Validate the save before attempting restore
fn save_is_loadable(text: &str) -> bool {
serde_json::from_str::<Saved>(text)
.map(|v| v.version == 1 && uuid::Uuid::parse_str(&v.identity).is_ok())
.unwrap_or(false)
} Try / catch
match run_world(&root) {
Err(e) if e.to_string().contains("could not restore") => {
// save file was kept; quarantine or repair it, then retry
archive_save_and_start_fresh(&root)?;
}
r => r?,
} Prevention
- Never hand-edit the habitat save file; keep schema migrations forward-compatible
- Back up the save before upgrading Codewhale versions
- Watch for eval errors at restore time in logs — the mapped error hides the root cause
When it happens
Trigger: ctx.eval of the restore script fails — e.g. the saved JSON deserialized but does not match what pet.restoreRecording expects (corrupt or schema-drifted save), the JS engine errored, or saved was absent/malformed despite passing the earlier version/uuid checks.
Common situations: A save written by a newer Codewhale version with a changed recording format; truncated or hand-edited habitat state file; JS runtime failure mid-restore after an earlier crash.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e7182ef2bb1b9f39.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/pet_watch/owner.rs:445
.map(|line| {
line.split_whitespace()
.filter_map(|n| n.parse().ok())
.collect()
})
.collect();
ctx.globals()
.set("points", serde_json::to_string(&points).unwrap())?;
ctx.eval::<(), _>(include_bytes!("pet-native.js").as_slice())?;
ctx.eval::<(), _>("globalThis.pet = new PetNative(points, '', '[]', true)")?;
if !saved.recording.is_null() {
ctx.globals().set("saved", saved.recording.to_string())?;
ctx.eval::<(), _>(
"pet.restoreRecording(saved); pet.resumeEngine(); delete globalThis.saved",
)?;
}
Ok(())
})
.map_err(|_| anyhow::anyhow!("Shared habitat could not restore; its file was kept"))?;
let mut producer: Option<(String, u64, Instant, String)> = None;
let mut audio: Option<(String, Instant)> = None;
let mut playback: Option<(super::audio::Output, super::audio_cursor::AudioCursor)> = None;
let mut audio_error = false;
let mut waiting = false;
let mut last_save = Instant::now();
let mut storage_error = false;
let origin = Instant::now();
let initial_time: f64 = context.with(|ctx| ctx.eval("JSON.parse(pet.snapshot()).timeMs"))?;
let mut last = origin;
let mut ticks = 0u64;
let mut measurements = VecDeque::<f64>::new();
save(&context, &mut saved, &mut store)?;
let _ = ready.send(Ok(()));
loop {
*deadline
.lock()
.map_err(|_| anyhow::anyhow!("Clock lock failed"))? =View on GitHub (pinned to 73e0f67d83)