{"record":{"id":"6aea484300b8f003","repo":"astrid-runtime/astrid","slug":"repl-history-exceeds-max-history-bytes-bytes","errorCode":null,"errorMessage":"REPL history exceeds {MAX_HISTORY_BYTES} bytes","messagePattern":"REPL history exceeds (.+?) bytes","errorType":"validation","errorClass":"std::io::Error::InvalidData","httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/repl.rs","lineNumber":268,"sourceCode":"        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound => {\n            astrid_core::platform_fs::atomic_write_private_file(new_path, &legacy)?;\n        },\n        Err(error) => return Err(error),\n    }\n    std::fs::remove_file(legacy_path)?;\n    Ok(())\n}\n\nfn read_history_bounded(path: &Path) -> io::Result<Vec<u8>> {\n    astrid_core::platform_fs::verify_no_redirects(path)?;\n    let mut file = File::open(path)?;\n    let mut bytes = Vec::new();\n    file.by_ref()\n        .take(MAX_HISTORY_BYTES.saturating_add(1))\n        .read_to_end(&mut bytes)?;\n    if bytes.len() as u64 > MAX_HISTORY_BYTES {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"REPL history exceeds {MAX_HISTORY_BYTES} bytes\"),\n        ));\n    }\n    Ok(bytes)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::migrate_legacy_history;\n\n    #[test]\n    fn legacy_history_moves_to_operator_log() {\n        let root = tempfile::tempdir().expect(\"tempdir\");\n        let legacy = root.path().join(\"history\");\n        let current = root.path().join(\"log/cli/history\");\n        std::fs::create_dir_all(current.parent().unwrap()).unwrap();\n        std::fs::write(&legacy, b\"/help\\nhello\\n\").unwrap();","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/repl.rs#L250-L286","documentation":"Thrown by read_history_bounded when the REPL history file is larger than the allowed MAX_HISTORY_BYTES cap. The function reads at most MAX_HISTORY_BYTES+1 bytes and returns InvalidData if the file did not fit, protecting memory from unbounded history files.","triggerScenarios":"Calling read_history_bounded (directly or via migrate_legacy_history) when the history file on disk exceeds MAX_HISTORY_BYTES bytes.","commonSituations":"A history file accumulated over years of REPL use, an imported/merged history from another tool, or a corrupted file that grew without bound.","solutions":["Delete or truncate the REPL history file to bring it under MAX_HISTORY_BYTES","Back up the history, trim old entries, and re-run the command","Raise MAX_HISTORY_BYTES if the cap is genuinely too small for your workflow"],"exampleFix":"// before: read unbounded\nlet mut file = File::open(path)?;\nfile.read_to_end(&mut bytes)?;\n// after: truncate oversized history or raise cap\nif metadata.len() > MAX_HISTORY_BYTES {\n    std::fs::write(path, b\"\")?; // reset oversized history\n}","handlingStrategy":"validation","validationCode":"if let Ok(md) = std::fs::metadata(&history_path) {\n    if md.len() > MAX_HISTORY_BYTES {\n        // truncate/back up before calling read_history_bounded\n        std::fs::write(&history_path, b\"\")?;\n    }\n}","typeGuard":null,"tryCatchPattern":"match read_history_bounded(&path) {\n    Ok(bytes) => use_history(bytes),\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => reset_history(&path),\n    Err(e) => return Err(e),\n}","preventionTips":["Rotate or cap history files in maintenance scripts","Monitor history file size in tooling","Import history through a truncating filter"],"tags":["io","file-size","repl"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}