langchain-ai/deepagents · error · ValueError

ReadResult window must satisfy 1 <= start_line <= end_line,

Error message

ReadResult window must satisfy 1 <= start_line <= end_line, got start_line={self.start_line}, end_line={self.end_line}

What it means

ReadResult.__post_init__ enforces that the line window satisfies 1 <= start_line <= end_line. Line numbers in ReadResult are 1-based; a backend that reports start_line < 1 or an end_line before the start produces an impossible window and is rejected with a ValueError at construction. This guards downstream consumers (middleware, line-number formatting) from negative or inverted ranges.

Source

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

            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)


@dataclass
class WriteResult:
    """Result from backend `write` operations.

    Attributes:
        error: Error message on failure, `None` on success.
        path: Absolute path of written file, `None` on failure.

    Examples:
        >>> WriteResult(path="/f.txt")

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Use 1-based numbering: start_line = offset + 1, end_line = offset + number_of_lines_returned.
  2. Assert in your backend that 1 <= start_line <= end_line before constructing ReadResult.
  3. If the result is empty, set start_line=None and end_line=None (or a valid degenerate window) rather than 0.

Example fix

// before
return ReadResult(content=text, start_line=offset, end_line=offset + len(lines))
// after
start = offset + 1
return ReadResult(content=text, start_line=start, end_line=start + len(lines) - 1)
Defensive patterns

Strategy: validation

Validate before calling

def check_window(start_line, end_line):
    if start_line is not None and end_line is not None:
        assert 1 <= start_line <= end_line, f'bad window {start_line}-{end_line}'

Type guard

def has_valid_window(r) -> bool:
    return r.start_line is None or (1 <= r.start_line <= (r.end_line or 0) + 1 and r.end_line >= r.start_line)

Try / catch

try:
    return ReadResult(content=text, start_line=start, end_line=end)
except ValueError:
    return ReadResult(content=text)  # degrade to unpaginated result

Prevention

When it happens

Trigger: Constructing ReadResult with start_line=0 (treating lines as 0-indexed), start_line=-1 (using offset directly as a 1-based line), or swapping the fields (end_line as start). E.g. ReadResult(content=t, start_line=offset, end_line=offset+limit) with offset=0.

Common situations: Confusing the backend `read(offset=...)` 0-based offset parameter with the 1-based start_line field; translating a 0-indexed slice range into the result fields without adding 1; custom backends and test fixtures.

Related errors


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