OpenBMB/ChatDev · error · ValueError

edit ranges overlap; merge them before calling apply_text_ed

Error message

edit ranges overlap; merge them before calling apply_text_edits

What it means

apply_text_edits requires edit ranges to be non-overlapping and sorted ascending; _validate_edit_ranges walks them in order and fails if an edit starts at or before the end of a previous edit (after accounting for pure deletions). Because edits are applied bottom-up in reverse order, overlapping ranges would corrupt positioning, so the library refuses them.

Source

Thrown at functions/function_calling/file.py:956

    start_line: int,
    end_line: Optional[int],
    replacement: Optional[str],
) -> List[Mapping[str, Any]]:
    effective_end = end_line if end_line is not None else start_line
    payload = {
        "start_line": start_line,
        "end_line": effective_end,
        "replacement": replacement if replacement is not None else "",
    }
    return [payload]


def _validate_edit_ranges(edits: Sequence[TextEdit]) -> None:
    previous_range_end = 0
    for edit in edits:
        effective_end = max(edit.end_line, edit.start_line - 1)
        if edit.start_line <= previous_range_end and previous_range_end > 0:
            raise ValueError("edit ranges overlap; merge them before calling apply_text_edits")
        previous_range_end = max(previous_range_end, effective_end)


def _apply_edits_in_place(lines: MutableSequence[str], edits: Sequence[TextEdit]) -> None:
    for edit in reversed(edits):
        current_line_count = len(lines)
        if edit.start_line > current_line_count + 1:
            raise ValueError("start_line is beyond the end of the file")
        start_idx = min(edit.start_line - 1, current_line_count)
        if start_idx > current_line_count:
            raise ValueError("start_line is beyond the end of the file")
        removal_count = max(edit.end_line - edit.start_line + 1, 0)
        if removal_count > 0:
            end_line = min(edit.end_line, len(lines))
            removal_count = max(end_line - edit.start_line + 1, 0)
        end_idx = start_idx + removal_count
        lines[start_idx:end_idx] = edit.replacement_lines

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Merge overlapping edits into a single edit covering the union range with the combined replacement text
  2. Sort edits by start_line ascending and verify ranges do not touch/overlap before calling
  3. Recompute edit ranges from the original file snapshot rather than incrementally from prior edits

Example fix

# before
edits=[{"start_line": 3, "end_line": 5, "replacement": "a"},
       {"start_line": 4, "end_line": 6, "replacement": "b"}]
# after
edits=[{"start_line": 3, "end_line": 6, "replacement": "a\nb"}]
Defensive patterns

Strategy: validation

Validate before calling

def merge_overlaps(edits):
    edits = sorted(edits, key=lambda e: e["start_line"])
    merged = []
    for e in edits:
        if merged and e["start_line"] <= merged[-1].get("end_line", merged[-1]["start_line"]):
            prev = merged.pop()
            e = {"start_line": prev["start_line"],
                 "end_line": max(e.get("end_line", 0), prev.get("end_line", 0)),
                 "replacement": e.get("replacement", "")}
        merged.append(e)
    return merged

apply_text_edits(path=path, edits=merge_overlaps(edits))

Type guard

def edits_disjoint_and_sorted(edits) -> bool:
    prev_end = 0
    for e in sorted(edits, key=lambda x: x["start_line"]):
        end = max(e.get("end_line", e["start_line"]), e["start_line"] - 1)
        if e["start_line"] <= prev_end > 0:
            return False
        prev_end = max(prev_end, end)
    return True

Try / catch

try:
    apply_text_edits(path=path, edits=edits)
except ValueError as e:
    if "overlap" in str(e):
        apply_text_edits(path=path, edits=merge_overlaps(edits))
    else:
        raise

Prevention

When it happens

Trigger: Two edits touching the same lines, e.g. (3-5) and (4-6); edits generated out of ascending order; edits derived from multiple diff hunks that share a boundary line.

Common situations: Merging hunks from a patch file without de-duplicating; LLM emitting redundant overlapping edits; adding an edit at the end of a list that should have been merged with an earlier one.

Related errors


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