langchain-ai/deepagents · error · ValueError

ReadResult.next_offset requires start_line and end_line to b

Error message

ReadResult.next_offset requires start_line and end_line to be set

What it means

ReadResult.__post_init__ requires next_offset to be accompanied by a real line window: next_offset without start_line (and thus end_line, per the paired invariant) is rejected with ValueError. This prevents backends from advertising a continuation cursor for a result that has no window to continue from.

Source

Thrown at libs/deepagents/deepagents/backends/protocol.py:258

        agree numerically: a window runs forward (`1 <= start_line <=
        end_line`), the file is at least as long as the window
        (`total_lines >= end_line`), and the resume point is the 0-indexed line
        immediately after the last one shown (`next_offset == end_line`, since
        `end_line` is 1-indexed). Fail loudly here to keep a backend from
        emitting a `next_offset` that would silently skip unshown source lines
        once it reaches the middleware.
        """
        if (self.start_line is None) != (self.end_line is None):
            msg = "ReadResult.start_line and end_line must be set together or both left unset"
            raise ValueError(msg)
        if self.no_lines_requested and (
            self.error is not None or self.start_line is not None or self.next_offset is not None or self.total_lines is not None
        ):
            msg = "ReadResult.no_lines_requested describes an uninspected window; it cannot be combined with error or pagination fields"
            raise ValueError(msg)
        if self.next_offset is not None and self.start_line is None:
            msg = "ReadResult.next_offset requires start_line and end_line to be set"
            raise ValueError(msg)
        if self.total_lines is not None and self.start_line is None:
            msg = "ReadResult.total_lines requires start_line and end_line to be set"
            raise ValueError(msg)

        # Numeric consistency of a present window. `start_line`/`end_line` are
        # bound together above, so testing `start_line` covers both.
        if self.start_line is not None and self.end_line is not None:
            if self.start_line < 1 or self.end_line < self.start_line:
                msg = f"ReadResult window must satisfy 1 <= start_line <= end_line, got start_line={self.start_line}, end_line={self.end_line}"
                raise ValueError(msg)
            if self.total_lines is not None and self.total_lines < self.end_line:
                msg = f"ReadResult.total_lines ({self.total_lines}) cannot be less than end_line ({self.end_line})"
                raise ValueError(msg)
            if self.next_offset is not None and self.next_offset != self.end_line:
                msg = f"ReadResult.next_offset ({self.next_offset}) must equal end_line ({self.end_line}), the 0-indexed line after the last shown"
                raise ValueError(msg)

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Set start_line and end_line whenever you set next_offset
  2. Drop next_offset if the result is not a line-windowed read
  3. Consult protocol.py's ReadResult invariants and align your backend's read implementation

Example fix

# before
return ReadResult(content=chunk, next_offset=51)
# after
return ReadResult(content=chunk, start_line=1, end_line=50, next_offset=51)
Defensive patterns

Strategy: type-guard

Validate before calling

if next_offset is not None:
    assert start_line is not None and end_line is not None, "next_offset requires a window"

Type guard

def can_continue(r: ReadResult) -> bool:
    return r.next_offset is not None and r.start_line is not None and r.end_line is not None

Try / catch

try:
    return ReadResult(content=chunk, next_offset=n)
except ValueError:
    return ReadResult(content=chunk, start_line=1, end_line=len(chunk.splitlines()), next_offset=n)

Prevention

When it happens

Trigger: Constructing ReadResult(content=..., next_offset=51) without start_line/end_line, typically from a backend implementing pagination incorrectly.

Common situations: Custom backend returning a truncated-content result with a continuation offset but forgetting to compute the line bounds that the offset was derived from.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/fc85501dd1be4416. Report an issue: GitHub.