{"record":{"id":"9ef815c1547f6fe2","repo":"pydantic/monty","slug":"host-read-bytes-should-return-bytes","errorCode":null,"errorMessage":"host_read_bytes should return bytes","messagePattern":"host_read_bytes should return bytes","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty-fs/src/overlay.rs","lineNumber":581,"sourceCode":"/// Loads the current visible file content for append operations.\nfn existing_file_bytes(\n    state: &OverlayState,\n    relative: &str,\n    ctx: &MountContext<'_>,\n    vpath: &str,\n    budget: MemoryBudget,\n) -> Result<Vec<u8>, MountError> {\n    match state.get(relative) {\n        Some(OverlayEntry::File(file)) => {\n            budget.check(as_u64(file.content.len()))?;\n            Ok(file.content.clone())\n        }\n        Some(OverlayEntry::Deleted) => Ok(Vec::new()),\n        Some(OverlayEntry::RealFileRef(file_ref)) => {\n            let rel = checked_ref_path(file_ref, ctx, vpath)?;\n            match host_read_bytes(ctx.mount_dir, rel, vpath, budget)? {\n                MontyObject::Bytes(bytes) => Ok(bytes),\n                _ => unreachable!(\"host_read_bytes should return bytes\"),\n            }\n        }\n        Some(OverlayEntry::Directory { .. }) => {\n            Err(MountError::io_err(ErrorKind::IsADirectory, \"Is a directory\", vpath))\n        }\n        None => match resolve_real_path_state(vpath, ctx, OnLookupFailure::Propagate)? {\n            RealPathState::Present(rel) => match host_read_bytes(ctx.mount_dir, &rel, vpath, budget)? {\n                MontyObject::Bytes(bytes) => Ok(bytes),\n                _ => unreachable!(\"host_read_bytes should return bytes\"),\n            },\n            RealPathState::Missing => Ok(Vec::new()),\n        },\n    }\n}\n\n/// Rejects writes when the target path is an existing directory or a symlink.\n///\n/// On real filesystems, writing to a directory returns `EISDIR`; the overlay","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-fs/src/overlay.rs#L563-L599","documentation":"An `unreachable!()` panic in monty-fs's overlay filesystem: `host_read_bytes` is contractually expected to return `MontyObject::Bytes`, and any other variant means the host read path violated its return-type contract. This is an internal consistency check in overlay file reads (used by `append_bytes` via `existing_file_bytes`).","triggerScenarios":"Reading an overlay `RealFileRef` entry's bytes via `host_read_bytes` when it returns a non-Bytes `MontyObject` (e.g. Str or None) — only possible if the host read implementation or mount backend changed its return contract.","commonSituations":"Hit by Monty-fs contributors changing `host_read_bytes`, its backends (ReadWrite/ReadOnly mounts), or the MontyObject conversion at the mount boundary.","solutions":["Inspect `host_read_bytes` and each mount backend to find where a non-Bytes MontyObject can be returned.","Ensure the read path always wraps file contents in `MontyObject::Bytes`.","Add/extend monty-fs integration tests covering RealFileRef overlay reads for all mount modes."],"exampleFix":"// before (in host_read_bytes)\nOk(MontyObject::Str(contents))\n// after\nOk(MontyObject::Bytes(contents.into_bytes()))","handlingStrategy":"type-guard","validationCode":"// Contract check before consuming the read result:\n// assert every host_read_bytes return site constructs MontyObject::Bytes.","typeGuard":"fn as_bytes(obj: MontyObject) -> Option<Vec<u8>> {\n    match obj { MontyObject::Bytes(b) => Some(b), _ => None }\n}","tryCatchPattern":"match host_read_bytes(ctx.mount_dir, rel, vpath, budget)? {\n    MontyObject::Bytes(bytes) => Ok(bytes),\n    other => Err(MountError::internal(format!(\"host_read_bytes returned {other:?}, expected bytes\"))),\n}","preventionTips":["Keep host_read_bytes' return type narrowly Bytes and enforce it in every mount backend.","Add overlay RealFileRef read tests for ReadWrite, ReadOnly, and OverlayMemory modes.","Avoid widening the return type to MontyObject without updating all consumers."],"tags":["rust","internal-error","panic","filesystem","overlay"],"backgroundTag":"internal-invariant-violation","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}