{"record":{"id":"42e11c4143ba203a","repo":"langchain-ai/deepagents","slug":"readresult-start-line-and-end-line-must-be-set-tog","errorCode":null,"errorMessage":"ReadResult.start_line and end_line must be set together or both left unset","messagePattern":"ReadResult\\.start_line and end_line must be set together or both left unset","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/backends/protocol.py","lineNumber":250,"sourceCode":"    \"\"\"\n\n    def __post_init__(self) -> None:\n        \"\"\"Reject malformed pagination-field combinations at construction.\n\n        The window fields are not independent: `start_line`/`end_line` are a\n        pair, and neither `next_offset` nor `total_lines` describes anything\n        without the window it refers to. Beyond co-presence, the values must\n        agree numerically: a window runs forward (`1 <= start_line <=\n        end_line`), the file is at least as long as the window\n        (`total_lines >= end_line`), and the resume point is the 0-indexed line\n        immediately after the last one shown (`next_offset == end_line`, since\n        `end_line` is 1-indexed). Fail loudly here to keep a backend from\n        emitting a `next_offset` that would silently skip unshown source lines\n        once it reaches the middleware.\n        \"\"\"\n        if (self.start_line is None) != (self.end_line is None):\n            msg = \"ReadResult.start_line and end_line must be set together or both left unset\"\n            raise ValueError(msg)\n        if self.no_lines_requested and (\n            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\n        ):\n            msg = \"ReadResult.no_lines_requested describes an uninspected window; it cannot be combined with error or pagination fields\"\n            raise ValueError(msg)\n        if self.next_offset is not None and self.start_line is None:\n            msg = \"ReadResult.next_offset requires start_line and end_line to be set\"\n            raise ValueError(msg)\n        if self.total_lines is not None and self.start_line is None:\n            msg = \"ReadResult.total_lines requires start_line and end_line to be set\"\n            raise ValueError(msg)\n\n        # Numeric consistency of a present window. `start_line`/`end_line` are\n        # bound together above, so testing `start_line` covers both.\n        if self.start_line is not None and self.end_line is not None:\n            if self.start_line < 1 or self.end_line < self.start_line:\n                msg = f\"ReadResult window must satisfy 1 <= start_line <= end_line, got start_line={self.start_line}, end_line={self.end_line}\"\n                raise ValueError(msg)","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/backends/protocol.py#L232-L268","documentation":"ReadResult is a dataclass with invariants enforced in __post_init__: start_line and end_line must both be set or both be None, because a half-specified window would produce a next_offset that silently skips source lines. Backends constructing ReadResult incorrectly trigger this ValueError immediately.","triggerScenarios":"A custom backend calling ReadResult(..., start_line=1) without end_line, or setting end_line without start_line.","commonSituations":"Writing a custom FilesystemBackend or middleware that paginates reads and only fills in one of the two bounds; refactors that changed field names but not both call sites.","solutions":["Set both start_line and end_line together when returning a paginated window","Leave both unset when the read is not line-windowed","Review the ReadResult docstring/protocol invariants and update your backend construction","Add a unit test constructing your ReadResult values so post_init failures surface in CI"],"exampleFix":"# before\nreturn ReadResult(content=body, start_line=1, next_offset=101)\n# after\nreturn ReadResult(content=body, start_line=1, end_line=100, next_offset=101)","handlingStrategy":"type-guard","validationCode":"def valid_read_result_kwargs(**kw) -> bool:\n    return (kw.get(\"start_line\") is None) == (kw.get(\"end_line\") is None)","typeGuard":"def has_full_window(r: ReadResult) -> bool:\n    return r.start_line is not None and r.end_line is not None","tryCatchPattern":"try:\n    result = ReadResult(content=body, start_line=s, end_line=e, next_offset=n)\nexcept ValueError:\n    result = ReadResult(content=body)  # no window, no pagination","preventionTips":["Always compute both line bounds together in backend read paths","Unit-test your backend's ReadResult construction so post_init fires in CI","Review ReadResult's documented invariants before adding fields"],"tags":["python","dataclass-invariant","protocol","pagination"],"backgroundTag":"invariant-violation","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}