OpenBMB/ChatDev · error · ValueError

end_line must be >= start_line - 1

Error message

end_line must be >= start_line - 1

What it means

After coercion, each edit must satisfy end_line >= start_line - 1. The -1 slack permits pure-deletion edits where end_line = start_line - 1 (delete starting at start_line with empty replacement); anything smaller means the range is inverted or nonsensical. This usually indicates swapped start/end or garbled diff math.

Source

Thrown at functions/function_calling/file.py:920

    if not edits:
        raise ValueError("at least one edit instruction is required")
    normalized: List[TextEdit] = []
    for item in edits:
        if not isinstance(item, Mapping):
            raise ValueError("each edit entry must be a mapping object")
        try:
            start_line = int(item["start_line"])
        except (KeyError, TypeError, ValueError) as exc:
            raise ValueError("start_line is required for each edit") from exc
        end_line_raw = item.get("end_line", start_line)
        try:
            end_line = int(end_line_raw)
        except (TypeError, ValueError) as exc:
            raise ValueError("end_line must be an integer") from exc
        if start_line < 1:
            raise ValueError("start_line must be >= 1")
        if end_line < start_line - 1:
            raise ValueError("end_line must be >= start_line - 1")
        replacement = item.get("replacement", "")
        if not isinstance(replacement, str):
            raise ValueError("replacement must be a string")
        normalized.append(
            TextEdit(
                start_line=start_line,
                end_line=end_line,
                replacement_lines=replacement.splitlines(),
            )
        )

    normalized.sort(key=lambda edit: (edit.start_line, edit.end_line))
    _validate_edit_ranges(normalized)
    return normalized


def _build_single_edit(
    start_line: int,

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Check whether start_line and end_line are swapped and correct them
  2. For deletions, use end_line = start_line - 1 with the appropriate replacement semantics, or better, set end_line to the last line to delete
  3. Validate ordering (end_line >= start_line - 1) per edit before calling

Example fix

# before
apply_text_edits(path="a.py", edits=[{"start_line": 10, "end_line": 2, "replacement": "x"}])
# after
apply_text_edits(path="a.py", edits=[{"start_line": 2, "end_line": 10, "replacement": "x"}])
Defensive patterns

Strategy: validation

Validate before calling

for e in edits:
    if e.get("end_line", e["start_line"]) < e["start_line"] - 1:
        e["start_line"], e["end_line"] = e["end_line"], e["start_line"]  # un-swap
apply_text_edits(path=path, edits=edits)

Type guard

def ranges_ordered(edits) -> bool:
    return all(
        e.get("end_line", e["start_line"]) >= e["start_line"] - 1 for e in edits
    )

Try / catch

try:
    apply_text_edits(path=path, edits=edits)
except ValueError as e:
    if "end_line must be >= start_line - 1" in str(e):
        raise ValueError(f"inverted range in edits: {edits}") from e
    raise

Prevention

When it happens

Trigger: Swapping start_line and end_line (start=10, end=2); decrementing end_line below start_line-1 during range arithmetic; expressing a deletion with end_line = start_line - 2.

Common situations: Diff parsers that emit ranges in file-order vs hunk-order inconsistently; LLM-generated edits with inverted ranges; code that clamps end_line incorrectly (e.g. min(end, start-2)).

Related errors


AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27). Data as JSON: /api/errors/50e480ec44ce4e11. Report an issue: GitHub.