sgl-project/sglang · error · ValueError

only one of 'replacement', 'prepend', 'append' may be set, g

Error message

only one of 'replacement', 'prepend', 'append' may be set, got: {', '.join(active)}

What it means

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.

Source

Thrown at python/sglang/srt/debug_utils/source_patcher/types.py:38

    Use ``prepend`` to keep the matched text and add lines before it.
    Use ``append`` to keep the matched text and add lines after it.
    Only one of ``replacement``, ``prepend``, and ``append`` may be set.
    """

    match: str
    replacement: str = ""
    prepend: str = ""
    append: str = ""

    @model_validator(mode="after")
    def _check_modes_mutually_exclusive(self) -> "EditSpec":
        active: list[str] = [
            name
            for name in ("replacement", "prepend", "append")
            if getattr(self, name).strip()
        ]
        if len(active) > 1:
            raise ValueError(
                f"only one of 'replacement', 'prepend', 'append' may be set, "
                f"got: {', '.join(active)}"
            )
        return self


class PatchSpec(_StrictBase):
    target: str
    edits: list[EditSpec]
    preamble: str = ""


class PatchConfig(_StrictBase):
    patches: list[PatchSpec]


class PatchState:
    def __init__(

View on GitHub (pinned to 0132848349)

Solutions

  1. Keep exactly one of `replacement`/`prepend`/`append` populated per edit and clear the others (set to "")
  2. If you need prepend AND replace, split into two sequential edits each with its own match anchor
  3. The error message lists the offending field names — remove all but the one you intend

Example fix

// before
Edit(match=m, replacement="new body", prepend="# comment\n")
// after
Edit(match=m, replacement="# comment\nnew body")
Defensive patterns

Strategy: validation

Validate before calling

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}')

Type guard

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

Try / catch

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, '')

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/6c9b13d02f3e642e. Report an issue: GitHub.