{"record":{"id":"9998f68e2fa894eb","repo":"Hmbown/CodeWhale","slug":"finite-points","errorCode":null,"errorMessage":"finite points","messagePattern":"finite points","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/worker.rs","lineNumber":129,"sourceCode":"    let deadline = Arc::new(Mutex::new(Instant::now() + Duration::from_secs(2)));\n    let check = Arc::clone(&deadline);\n    runtime.set_interrupt_handler(Some(Box::new(move || {\n        check.lock().map_or(true, |d| Instant::now() > *d)\n    })));\n    let context = Context::full(&runtime).map_err(|_| ())?;\n    context\n        .with(|ctx| -> rquickjs::Result<()> {\n            let points: Vec<Vec<f64>> = include_str!(\"../ambient_life/whale-points.tsv\")\n                .lines()\n                .map(|line| {\n                    line.split_whitespace()\n                        .filter_map(|s| s.parse().ok())\n                        .collect()\n                })\n                .collect();\n            ctx.globals().set(\n                \"points\",\n                serde_json::to_string(&points).expect(\"finite points\"),\n            )?;\n            ctx.eval::<(), _>(include_bytes!(\"pet-native.js\").as_slice())?;\n            ctx.eval::<(), _>(\"globalThis.pet = new PetNative(points, '', '[]', true)\")?;\n            Ok(())\n        })\n        .map_err(|_| ())?;\n    let mut store = None;\n    let mut offset_ms = 0.0;\n    if let Some(session) = session {\n        // Hydrating a bounded recording validates its whole accepted history.\n        // It runs off the UI thread and can take longer than a frame command.\n        *deadline.lock().map_err(|_| ())? = Instant::now() + Duration::from_secs(10);\n        let loaded = (|| -> Result<Store, ()> {\n            let mut files = Store::open(session).map_err(|_| ())?;\n            if let Some(saved) = files.load().map_err(|_| ())? {\n                offset_ms = context\n                    .with(|ctx| -> rquickjs::Result<f64> {\n                        ctx.globals().set(\"savedHabitat\", saved)?;","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/worker.rs#L111-L147","documentation":"Panic from `.expect(\"finite points\")` on `serde_json::to_string(&points)` inside the pet-watch QuickJS worker (crates/tui/src/tui/pet_watch/worker.rs:129). The points are parsed from the bundled `whale-points.tsv` as `f64`; `serde_json::to_string` errors when a value is NaN or infinite (JSON has no representation for them). This is a build-data invariant: the shipped TSV must contain only finite floats.","triggerScenarios":"`whale-points.tsv` (or any replacement points file) containing a token that parses to NaN/inf, or a code change altering the parser to emit non-finite defaults (e.g. `parse().unwrap_or(f64::NAN)`); the panic fires at worker `start()`/`run()` when the JS pet engine is initialized.","commonSituations":"Editing or regenerating the ambient-life points dataset with an export script that emits `NaN`/`Inf`; fuzzing or replacing the TSV in tests; hand-editing the resource with locale-formatted decimals (`1,5`) that a filter_map silently drops, followed by a later change substituting non-finite fallbacks.","solutions":["Audit `crates/tui/src/tui/ambient_life/whale-points.tsv` for NaN/Inf-producing values and regenerate it with finite numbers only.","Add a build-time check that parses the TSV and asserts every cell is finite, so bad data fails the build instead of the worker.","If the parser must tolerate malformed lines, drop them (`.filter(|v| v.is_finite())`) rather than substituting non-finite defaults.","Replace the bare `.expect(\"finite points\")` with an expect that names the offending cell/line for faster diagnosis."],"exampleFix":"// before\nserde_json::to_string(&points).expect(\"finite points\")\n// after\nserde_json::to_string(&points)\n    .expect(\"whale-points.tsv must contain only finite floats (json serialize)\")","handlingStrategy":"validation","validationCode":"let points: Vec<Vec<f64>> = /* parsed TSV */;\nassert!(points.iter().flatten().all(|v| v.is_finite()), \"non-finite point in whale-points.tsv\");","typeGuard":"fn all_finite(points: &[Vec<f64>]) -> bool { points.iter().flatten().all(|v| v.is_finite()) }","tryCatchPattern":"let json = serde_json::to_string(&points)\n    .expect(\"finite points (check whale-points.tsv for NaN/Inf)\");","preventionTips":["Add a build/test assertion that every parsed TSV value is finite.","Drop (not default) malformed TSV lines in the parser.","Regenerate datasets with scripts that serialize finite f64 only."],"tags":["serde","json","invariant","panic","quickjs"],"backgroundTag":"json-serialization-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T10:30:35.592Z"}