OpenBMB/ChatDev · error · ValueError
each edit entry must be a mapping object
Error message
each edit entry must be a mapping object
What it means
Each element of the edits sequence passed to apply_text_edits must be a mapping (dict-like) with start_line/end_line/replacement keys. _normalize_edits checks isinstance(item, Mapping) and raises ValueError when an entry is, e.g., a string, tuple, or list. This guards against malformed tool payloads before any file is touched.
Source
Thrown at functions/function_calling/file.py:907
preserve_trailing_newline: bool,
) -> str:
numbered: List[str] = []
for idx, line in enumerate(lines):
body = line.rstrip("\r\n")
numbered.append(f"{start_line + idx}:{body}")
rendered = newline_style.join(numbered)
if preserve_trailing_newline and numbered:
rendered += newline_style
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(View on GitHub (pinned to 4fb2db0ea9)
Solutions
- Ensure every edit entry is a dict with at least start_line and optionally end_line/replacement
- If JSON-serializing edits, use objects: {"start_line": 3, "end_line": 3, "replacement": "..."} not arrays
- Validate/normalize the payload shape before calling apply_text_edits
Example fix
# before
apply_text_edits(path="a.py", edits=["3: new text"])
# after
apply_text_edits(path="a.py", edits=[{"start_line": 3, "replacement": "new text"}]) Defensive patterns
Strategy: type-guard
Validate before calling
if not all(isinstance(e, dict) for e in edits):
edits = [e if isinstance(e, dict) else dict(e) for e in edits] # or reject
apply_text_edits(path=path, edits=edits) Type guard
from collections.abc import Mapping
def is_edit_list(edits) -> bool:
return isinstance(edits, (list, tuple)) and all(isinstance(e, Mapping) for e in edits) Try / catch
try:
apply_text_edits(path=path, edits=edits)
except ValueError as e:
raise TypeError(f"malformed edits payload: {e}") from e Prevention
- Define a JSON schema requiring each edit to be an object with required keys
- Validate LLM/tool payloads before invoking file-mutating tools
- Never pass tuples/strings as edit entries
When it happens
Trigger: Passing edits as a list of strings ('replace line 3'), tuples, or JSON where each entry is an array instead of an object; double-wrapping edits as [[{...}]] so inner items are lists; deserializing JSON with entries that are not objects.
Common situations: LLM tool-call arguments that encode edits as arrays; converting between tuple-based and dict-based edit representations; copy-pasting an example where the edit body was a string instead of an object.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- end_line must be an integer
- replacement must be a string
- at least one edit instruction is required
- start_line is required for each edit
- end_line must be >= start_line - 1
AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27).
Data as JSON: /api/errors/ee71ef907c59eb63.
Report an issue: GitHub.