OpenBMB/ChatDev · error · ValueError

end_line must be an integer

Error message

end_line must be an integer

What it means

The optional end_line field, when present, must be coercible to an integer. _normalize_edits runs int(end_line_raw) and converts TypeError/ValueError into this error, e.g. None, a non-numeric string, or a nested object. Note that int() accepts numeric strings like "12", so only genuinely non-integer values fail.

Source

Thrown at functions/function_calling/file.py:916

    return rendered


def _normalize_edits(edits: Sequence[Mapping[str, Any]]) -> List[TextEdit]:
    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

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Pass end_line as an integer or omit it (it defaults to start_line)
  2. To extend to end of file, pass the actual last line number, not null or 'EOF'
  3. Validate that end_line is an int or int-coercible string before calling

Example fix

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

Strategy: validation

Validate before calling

for e in edits:
    if "end_line" in e and e["end_line"] is None:
        del e["end_line"]  # default to start_line
apply_text_edits(path=path, edits=edits)

Type guard

def end_lines_valid(edits) -> bool:
    return all(
        e.get("end_line", e.get("start_line")) is not None
        and str(e.get("end_line", 0)).lstrip("-").replace(".", "", 1).isdigit()
        for e in edits
    )

Try / catch

try:
    apply_text_edits(path=path, edits=edits)
except ValueError as e:
    if "end_line must be an integer" in str(e):
        for ed in edits:
            if "end_line" in ed: ed["end_line"] = int(ed["end_line"])
        apply_text_edits(path=path, edits=edits)
    else:
        raise

Prevention

When it happens

Trigger: Passing end_line=None; end_line="5-7" or "abc"; end_line as a dict/list from malformed JSON; float strings like "1.5" (int('1.5') raises ValueError).

Common situations: LLM tool args with stringly-typed line ranges; JSON payloads where end_line was serialized as a range string; schemas that declare end_line as any/nullable and callers pass null meaning 'to EOF' (not supported).

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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