sinelaw/fresh · critical

Failed to read data at offset

Error message

Failed to read data at offset {}: no progress made (requested {}..{}, buffer len: {})

What it means

The piece-tree buffer's read path walks chunks to satisfy a byte-range read. If an iteration over the piece tree makes no forward progress (current_offset doesn't advance toward end_offset), an internal invariant has been broken — continuing would loop forever — so the code logs piece-tree stats and bails with 'Failed to read data at offset ...: no progress made'. This indicates a corrupted piece tree or a bug, not bad user input.

Solutions

  1. Report/inspect as a bug: capture the logged piece-tree stats (len, total_bytes) and the offset range from the message.
  2. Reload the buffer from disk (re-open the file) to rebuild a clean piece tree.
  3. Verify callers pass offsets derived from the buffer's current len() and refresh cached offsets after edits.
  4. If reproducible, reduce to a minimal edit/undo sequence and file an issue with the reproduction steps.

Example fix

// before: stale offset cached before a deletion
let off = buffer.len();
buffer.delete(...);
buffer.read_data(off..off + n)?; // no-progress bail

// after: clamp to the current length at read time
let off = off.min(buffer.len());
let end = (off + n).min(buffer.len());
buffer.read_data(off..end)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: clamp read ranges to the buffer's current length
let off = offset.min(buffer.len());
let end = end_offset.min(buffer.len());
if off >= end { return Ok(Vec::new()); }

Try / catch

match buffer.read_data(range) {
    Err(e) if e.to_string().contains("no progress made") => {
        tracing::error!("piece tree invariant violation: {e}");
        // recover by reloading the buffer from disk
        buffer = Buffer::load_file(path)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Requesting read_data (or a bulk read spanning ranges) at an offset/end range when the chunk walk fails to advance current_offset — typically a piece-tree invariant violation: stale piece references after edits, an offset beyond current length slipping past earlier checks, or a deletion/undo that left empty/zero-length pieces in the walk.

Common situations: After complex undo/redo or concurrent edit sequences that corrupt piece ordering; extension/API code caching offsets and reading after the buffer shrank; genuinely a library bug — users rarely trigger it intentionally.

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/022a4cd1a9dc69fc. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor-core/src/model/buffer/mod.rs:1438

                    current_offset = read_end;
                    made_progress = true;
                }
            }

            // If we didn't make progress and didn't restart iteration, this is an error
            if !made_progress && !restarted_iteration {
                tracing::error!(
                    "get_text_range_mut: No progress at offset {} (requested range: {}..{}, buffer len: {})",
                    current_offset,
                    offset,
                    end_offset,
                    self.len()
                );
                tracing::error!(
                    "Piece tree stats: {} total bytes",
                    self.piece_tree.stats().total_bytes
                );
                anyhow::bail!(
                    "Failed to read data at offset {}: no progress made (requested {}..{}, buffer len: {})",
                    current_offset,
                    offset,
                    end_offset,
                    self.len()
                );
            }
        }

        if iteration_count > 1 {
            tracing::info!(
                iteration_count,
                result_len = result.len(),
                "get_text_range_mut: completed with multiple iterations"
            );
        }

        crate::counters::work::add_buffer_bytes_read(result.len() as u64);

View on GitHub (pinned to 67894ca546)