sinelaw/fresh · error · io::Error (InvalidData)

Buffer not found

Error message

Buffer {} not found

What it means

build_write_recipe fails while walking pieces of the piece tree when a piece references a buffer_id missing from the buffers map. Since the recipe must copy bytes from each source buffer, a missing buffer makes the save recipe unbuildable. Reported as InvalidData because the recipe input is inconsistent.

Solutions

  1. Keep referenced buffers alive until the save completes (defer removal).
  2. Log which buffer_id is missing and fix the code path that removed it prematurely.
  3. Validate all piece buffer_ids before building the recipe.
Defensive patterns

Strategy: validation

Validate before calling

for piece in piece_tree.iter_pieces_in_range(0, total) {
    assert!(buffers.contains_key(piece.location.buffer_id()), "piece references removed buffer");
}

Try / catch

match build_write_recipe(...) {
    Ok(recipe) => recipe,
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        // keep buffers alive until save completes, then retry
        reload_buffers();
        build_write_recipe(...)?
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling build_write_recipe (via save) where iter_pieces_in_range yields a piece whose location.buffer_id() is not in `buffers` — typically after a buffer was unloaded/removed while still referenced by pieces.

Common situations: Saving a document assembled from pieces of a since-closed buffer; lifecycle bug where removal precedes save.

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 sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/c610e07d1fb86743. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor-core/src/model/buffer/save.rs:196

            .file_path()
            .filter(|p| persistence.fs().exists(p))
    };
    let target_ending = format.line_ending();
    let target_encoding = format.encoding();

    let mut insert_data: Vec<Vec<u8>> = Vec::new();
    let mut actions: Vec<RecipeAction> = Vec::new();

    // Add BOM as the first piece if the target encoding has one
    if let Some(bom) = target_encoding.bom_bytes() {
        insert_data.push(bom.to_vec());
        actions.push(RecipeAction::Insert { index: 0 });
    }

    for piece_view in piece_tree.iter_pieces_in_range(0, total) {
        let buffer_id = piece_view.location.buffer_id();
        let buffer = buffers.get(buffer_id).ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                format!("Buffer {} not found", buffer_id),
            )
        })?;

        match &buffer.data {
            // Unloaded buffer: can use Copy if same source file, else load and send
            BufferData::Unloaded {
                file_path,
                file_offset,
                ..
            } => {
                // Can only use Copy if:
                // - This is a Stored piece (original file content)
                // - We have a valid source for copying
                // - This buffer is from that source
                // - No line ending or encoding conversion needed
                let can_copy = matches!(piece_view.location, BufferLocation::Stored(_))

View on GitHub (pinned to 67894ca546)