pydantic/monty · error

host_read_bytes should return bytes

Error message

host_read_bytes should return bytes

What it means

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`).

Source

Thrown at crates/monty-fs/src/overlay.rs:581

/// Loads the current visible file content for append operations.
fn existing_file_bytes(
    state: &OverlayState,
    relative: &str,
    ctx: &MountContext<'_>,
    vpath: &str,
    budget: MemoryBudget,
) -> Result<Vec<u8>, MountError> {
    match state.get(relative) {
        Some(OverlayEntry::File(file)) => {
            budget.check(as_u64(file.content.len()))?;
            Ok(file.content.clone())
        }
        Some(OverlayEntry::Deleted) => Ok(Vec::new()),
        Some(OverlayEntry::RealFileRef(file_ref)) => {
            let rel = checked_ref_path(file_ref, ctx, vpath)?;
            match host_read_bytes(ctx.mount_dir, rel, vpath, budget)? {
                MontyObject::Bytes(bytes) => Ok(bytes),
                _ => unreachable!("host_read_bytes should return bytes"),
            }
        }
        Some(OverlayEntry::Directory { .. }) => {
            Err(MountError::io_err(ErrorKind::IsADirectory, "Is a directory", vpath))
        }
        None => match resolve_real_path_state(vpath, ctx, OnLookupFailure::Propagate)? {
            RealPathState::Present(rel) => match host_read_bytes(ctx.mount_dir, &rel, vpath, budget)? {
                MontyObject::Bytes(bytes) => Ok(bytes),
                _ => unreachable!("host_read_bytes should return bytes"),
            },
            RealPathState::Missing => Ok(Vec::new()),
        },
    }
}

/// Rejects writes when the target path is an existing directory or a symlink.
///
/// On real filesystems, writing to a directory returns `EISDIR`; the overlay

View on GitHub (pinned to adc986b362)

Solutions

  1. Inspect `host_read_bytes` and each mount backend to find where a non-Bytes MontyObject can be returned.
  2. Ensure the read path always wraps file contents in `MontyObject::Bytes`.
  3. Add/extend monty-fs integration tests covering RealFileRef overlay reads for all mount modes.

Example fix

// before (in host_read_bytes)
Ok(MontyObject::Str(contents))
// after
Ok(MontyObject::Bytes(contents.into_bytes()))
Defensive patterns

Strategy: type-guard

Validate before calling

// Contract check before consuming the read result:
// assert every host_read_bytes return site constructs MontyObject::Bytes.

Type guard

fn as_bytes(obj: MontyObject) -> Option<Vec<u8>> {
    match obj { MontyObject::Bytes(b) => Some(b), _ => None }
}

Try / catch

match host_read_bytes(ctx.mount_dir, rel, vpath, budget)? {
    MontyObject::Bytes(bytes) => Ok(bytes),
    other => Err(MountError::internal(format!("host_read_bytes returned {other:?}, expected bytes"))),
}

Prevention

When it happens

Trigger: 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.

Common situations: Hit by Monty-fs contributors changing `host_read_bytes`, its backends (ReadWrite/ReadOnly mounts), or the MontyObject conversion at the mount boundary.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/9ef815c1547f6fe2. Report an issue: GitHub.