OpenBMB/ChatDev · error · ValueError
at least one edit instruction is required
Error message
at least one edit instruction is required
What it means
apply_text_edits normalizes its edits list via _normalize_edits, which requires a non-empty sequence. Passing an empty list (or None-like empty sequence) means there is nothing to apply, so the library rejects it rather than silently writing the file unchanged. Provide at least one edit instruction.
Solutions
- Skip the apply_text_edits call entirely when the edits list is empty
- Ensure your edit-generation step (diff parsing, template diffing) always emits at least one edit or signals no-op
- Default to early-return: if not edits: return unchanged result
Example fix
# before
apply_text_edits(path="a.py", edits=[])
# after
if edits:
apply_text_edits(path="a.py", edits=edits) Defensive patterns
Strategy: validation
Validate before calling
if not edits:
return {"changed": False}
apply_text_edits(path=path, edits=edits) Type guard
def has_edits(edits) -> bool:
return bool(edits) and len(edits) > 0 Try / catch
try:
apply_text_edits(path=path, edits=edits)
except ValueError as e:
if "at least one edit" in str(e):
return {"changed": False}
raise Prevention
- Short-circuit no-op diffs before calling
- When parsing patches, treat zero hunks as 'file unchanged'
- Assert non-empty edit list in debug builds
When it happens
Trigger: Calling apply_text_edits(path, edits=[]) or edits=(); building edits from a diff/patch parser that produced zero hunks; filtering an edits list and accidentally emptying it before the call.
Common situations: Programmatic edit pipelines where a no-op diff (identical old/new content) yields an empty edit list; conditional code paths that accumulate edits but skip appending; LLM tool-call payloads with an empty edits array.
Related errors
- each edit entry must be a mapping object
- edit ranges overlap; merge them before calling…
- end_line must be an integer
- end_line must be >= start_line - 1
- replacement must be a string
AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27).
Data as JSON: /api/errors/96dca9c391eb480a.
Report an issue: GitHub.
Appendix: source
Thrown at functions/function_calling/file.py:903
def _render_snippet_with_line_numbers(
lines: Sequence[str],
start_line: int,
newline_style: str,
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", "")View on GitHub (pinned to 4fb2db0ea9)