{"record":{"id":"6c9b13d02f3e642e","repo":"sgl-project/sglang","slug":"only-one-of-replacement-prepend-append-may","errorCode":null,"errorMessage":"only one of 'replacement', 'prepend', 'append' may be set, got: {', '.join(active)}","messagePattern":"only one of 'replacement', 'prepend', 'append' may be set, got: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/debug_utils/source_patcher/types.py","lineNumber":38,"sourceCode":"    Use ``prepend`` to keep the matched text and add lines before it.\n    Use ``append`` to keep the matched text and add lines after it.\n    Only one of ``replacement``, ``prepend``, and ``append`` may be set.\n    \"\"\"\n\n    match: str\n    replacement: str = \"\"\n    prepend: str = \"\"\n    append: str = \"\"\n\n    @model_validator(mode=\"after\")\n    def _check_modes_mutually_exclusive(self) -> \"EditSpec\":\n        active: list[str] = [\n            name\n            for name in (\"replacement\", \"prepend\", \"append\")\n            if getattr(self, name).strip()\n        ]\n        if len(active) > 1:\n            raise ValueError(\n                f\"only one of 'replacement', 'prepend', 'append' may be set, \"\n                f\"got: {', '.join(active)}\"\n            )\n        return self\n\n\nclass PatchSpec(_StrictBase):\n    target: str\n    edits: list[EditSpec]\n    preamble: str = \"\"\n\n\nclass PatchConfig(_StrictBase):\n    patches: list[PatchSpec]\n\n\nclass PatchState:\n    def __init__(","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/debug_utils/source_patcher/types.py#L20-L56","documentation":"A patch/edit spec object validates that at most one of its three mutation modes — `replacement`, `prepend`, `append` — is non-empty. Setting two or more is contradictory (replace AND prepend what?) so _check_modes_mutually_exclusive raises ValueError at construction/validation time.","triggerScenarios":"Creating an edit spec with both `replacement` and `prepend` set (or any two of the three), including when one is set to a whitespace-only string that still passes `.strip()` truthiness checks in reverse — actually only non-blank strings count, so the trigger is two fields with non-blank content.","commonSituations":"Copy-pasting an existing edit spec and adding a new field without clearing the old one; refactoring code that used to allow combined semantics; programmatically filling fields from a dict that contains leftover keys.","solutions":["Keep exactly one of `replacement`/`prepend`/`append` populated per edit and clear the others (set to \"\")","If you need prepend AND replace, split into two sequential edits each with its own match anchor","The error message lists the offending field names — remove all but the one you intend"],"exampleFix":"// before\nEdit(match=m, replacement=\"new body\", prepend=\"# comment\\n\")\n// after\nEdit(match=m, replacement=\"# comment\\nnew body\")","handlingStrategy":"validation","validationCode":"active = [n for n in ('replacement','prepend','append') if getattr(edit, n).strip()]\\nif len(active) > 1:\\n    raise ValueError(f'configure only one mode, got {active}')","typeGuard":"def is_valid_edit(e) -> bool:\\n    active = [n for n in ('replacement','prepend','append') if getattr(e, n).strip()]\\n    return len(active) == 1","tryCatchPattern":"try:\\n    edit.validate()  # or construct inside try\\nexcept ValueError as e:\\n    # e lists offending fields; blank out all but the intended one\\n    for n in ('replacement','prepend','append'):\\n        if n not in intended:\\n            setattr(edit, n, '')","preventionTips":["Construct edit specs with only one mode argument keyword","When reusing spec objects, reset fields before assigning a new mode","Assert exactly one active mode in unit tests for spec builders"],"tags":["validation","edit-spec","mutually-exclusive","sglang"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}