{"record":{"id":"4921f752cb557c37","repo":"Hmbown/CodeWhale","slug":"event-recovery-newline-index-fits-u64","errorCode":null,"errorMessage":"event recovery newline index fits u64","messagePattern":"event recovery newline index fits u64","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":14030,"sourceCode":"    file.read_exact(&mut last)?;\n    if last[0] == b'\\n' {\n        return Ok(());\n    }\n\n    let mut search_end = len;\n    let mut truncate_at = 0_u64;\n    let mut buffer = [0_u8; 8 * 1024];\n    let buffer_len = u64::try_from(buffer.len()).expect(\"event recovery buffer fits u64\");\n    while search_end > 0 {\n        let chunk_len = usize::try_from(search_end.min(buffer_len))\n            .expect(\"event recovery chunk length fits usize\");\n        let chunk_len_u64 = u64::try_from(chunk_len).expect(\"event recovery chunk length fits u64\");\n        let chunk_start = search_end - chunk_len_u64;\n        file.seek(SeekFrom::Start(chunk_start))?;\n        file.read_exact(&mut buffer[..chunk_len])?;\n        if let Some(index) = buffer[..chunk_len].iter().rposition(|byte| *byte == b'\\n') {\n            truncate_at = chunk_start\n                + u64::try_from(index).expect(\"event recovery newline index fits u64\")\n                + 1;\n            break;\n        }\n        search_end = chunk_start;\n    }\n\n    file.set_len(truncate_at)\n        .with_context(|| format!(\"Failed to truncate torn tail in {}\", path.display()))?;\n    file.sync_all()\n        .with_context(|| format!(\"Failed to sync repaired {}\", path.display()))?;\n    tracing::warn!(\n        path = %path.display(),\n        removed_bytes = len.saturating_sub(truncate_at),\n        \"Recovered an unterminated Runtime event-log tail\"\n    );\n    Ok(())\n}\n","sourceCodeStart":14012,"sourceCodeEnd":14048,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L14012-L14048","documentation":"This panic comes from a `u64::try_from(index).expect(...)` inside the runtime event-log tail repair loop in `crates/tui/src/runtime_threads.rs`. After reading a trailing chunk of a possibly torn event log, the code finds the last newline byte with `rposition` and converts that byte index to u64 before computing the truncate offset. The expect documents the invariant that the index (bounded by an 8 KiB buffer) always fits in u64; on any 64-bit (and realistically 32-bit) platform it cannot fail, so a panic here signals a broken build/platform assumption or a corrupted internal invariant rather than bad input.","triggerScenarios":"Only reachable when the event-log file lacks a final newline, a chunk containing a newline is read, and `usize::try_into::<u64>()` fails for the byte index — i.e. running on a platform where usize is wider than u64 or under a broken compiler/memory model where the conversion is rejected.","commonSituations":"Developers essentially never hit this in production; it appears when porting the code to exotic targets (e.g. some CHERI / 128-bit-pointer platforms where usize is 128 bits) or when an assertion-based fuzzing/memory-check build flags the try_from as potentially failing.","solutions":["Treat the panic as a platform-portability finding: replace the expect with saturating/checked conversion or `as u64` only after a compile-time assert that `size_of::<usize>() <= size_of::<u64>()`.","If hit at runtime, inspect the panic backtrace to confirm which try_from failed and verify the target's pointer width; do not ship a build for that target until the conversion is made total.","If it fires on a normal 64-bit build, suspect memory corruption or a modified local copy of runtime_threads.rs and diff against the committed source."],"exampleFix":"// before\ntruncate_at = chunk_start\n    + u64::try_from(index).expect(\"event recovery newline index fits u64\")\n    + 1;\n// after\nconst _: () = assert!(size_of::<usize>() <= size_of::<u64>());\ntruncate_at = chunk_start + u64::try_from(index).unwrap_or(u64::MAX) + 1;","handlingStrategy":"validation","validationCode":"const _: () = assert!(size_of::<usize>() <= size_of::<u64>(), \"usize must fit in u64 for event-log offsets\");","typeGuard":"fn fits_u64(v: usize) -> Option<u64> { u64::try_from(v).ok() }","tryCatchPattern":null,"preventionTips":["Add a compile-time assert on pointer width when using try_from(usize)->u64 with expect.","On exotic targets, audit all usize->u64 conversions in offset math.","Keep conversion expects narrowly scoped to provably-bounded values and say why in the message."],"tags":["rust","panic","integer-conversion","platform-portability"],"backgroundTag":"value-out-of-range","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}