{"record":{"id":"354b3782c852beff","repo":"OpenBMB/ChatDev","slug":"replacement-must-be-a-string","errorCode":null,"errorMessage":"replacement must be a string","messagePattern":"replacement must be a string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"functions/function_calling/file.py","lineNumber":923,"sourceCode":"    for item in edits:\n        if not isinstance(item, Mapping):\n            raise ValueError(\"each edit entry must be a mapping object\")\n        try:\n            start_line = int(item[\"start_line\"])\n        except (KeyError, TypeError, ValueError) as exc:\n            raise ValueError(\"start_line is required for each edit\") from exc\n        end_line_raw = item.get(\"end_line\", start_line)\n        try:\n            end_line = int(end_line_raw)\n        except (TypeError, ValueError) as exc:\n            raise ValueError(\"end_line must be an integer\") from exc\n        if start_line < 1:\n            raise ValueError(\"start_line must be >= 1\")\n        if end_line < start_line - 1:\n            raise ValueError(\"end_line must be >= start_line - 1\")\n        replacement = item.get(\"replacement\", \"\")\n        if not isinstance(replacement, str):\n            raise ValueError(\"replacement must be a string\")\n        normalized.append(\n            TextEdit(\n                start_line=start_line,\n                end_line=end_line,\n                replacement_lines=replacement.splitlines(),\n            )\n        )\n\n    normalized.sort(key=lambda edit: (edit.start_line, edit.end_line))\n    _validate_edit_ranges(normalized)\n    return normalized\n\n\ndef _build_single_edit(\n    start_line: int,\n    end_line: Optional[int],\n    replacement: Optional[str],\n) -> List[Mapping[str, Any]]:","sourceCodeStart":905,"sourceCodeEnd":941,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/functions/function_calling/file.py#L905-L941","documentation":"The optional replacement field defaults to \"\" but, when provided, must be a str. _normalize_edits rejects lists of lines, bytes, None, or numbers, because replacement is later processed with .splitlines(). Provide replacement as a single string (newline-separated if multi-line).","triggerScenarios":"Passing replacement=[\"line1\", \"line2\"] (list of lines); replacement=None; replacement=b\"text\"; replacement=42 from malformed JSON.","commonSituations":"Callers that already split text into lines for other APIs; JSON payloads where replacement is an array; treating an omitted replacement and null replacement as equivalent (null fails, omission defaults to \"\").","solutions":["Join line lists into one string: \"\\n\".join(lines)","Use \"\" (or omit the key) for deletion instead of None","Validate isinstance(replacement, str) per edit before calling"],"exampleFix":"# before\nedits=[{\"start_line\": 1, \"replacement\": [\"a\", \"b\"]}]\n# after\nedits=[{\"start_line\": 1, \"replacement\": \"a\\nb\"}]","handlingStrategy":"type-guard","validationCode":"for e in edits:\n    if isinstance(e.get(\"replacement\"), list):\n        e[\"replacement\"] = \"\\n\".join(e[\"replacement\"])\n    elif e.get(\"replacement\") is None:\n        e[\"replacement\"] = \"\"\napply_text_edits(path=path, edits=edits)","typeGuard":"def replacements_are_strings(edits) -> bool:\n    return all(\n        not isinstance(e.get(\"replacement\", \"\"), (list, dict, bytes, type(None)))\n        for e in edits\n    )","tryCatchPattern":"try:\n    apply_text_edits(path=path, edits=edits)\nexcept ValueError as e:\n    if \"replacement must be a string\" in str(e):\n        for ed in edits:\n            if isinstance(ed.get(\"replacement\"), list):\n                ed[\"replacement\"] = \"\\n\".join(ed[\"replacement\"])\n        apply_text_edits(path=path, edits=edits)\n    else:\n        raise","preventionTips":["Pass replacement as one newline-joined string","Use \"\" for deletions, not None","Schema: replacement must be type string, not array"],"tags":["validation","text-edit","type-mismatch"],"backgroundTag":"invalid-argument-type","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}