CoplayDev/unity-mcp · error · RuntimeError

unknown edit op: {op}; allowed: {allowed}. Use 'op' (aliases

Error message

unknown edit op: {op}; allowed: {allowed}. Use 'op' (aliases accepted: type/mode/operation).

What it means

In script_apply_edits, after checking op is non-empty, the dispatch chain (prepend, append, anchor_insert, replace_range, regex_replace) is evaluated. If op matches none of these, the else branch fires listing the five allowed values. This means the caller provided a recognized-but-unsupported operation name, or a typo. Unlike error 176 (op missing), here op IS present but has an unrecognized value.

Source

Thrown at Server/src/services/tools/script_apply_edits.py:455

                if line <= len(lines):
                    return sum(len(l) for l in lines[: line - 1]) + (col - 1)
                return sum(len(l) for l in lines)
            a = index_of(start_line, start_col)
            b = index_of(end_line, end_col)
            text = text[:a] + replacement + text[b:]
        elif op == "regex_replace":
            pattern = edit.get("pattern", "")
            repl = edit.get("replacement", "")
            # Translate $n backrefs (our input) to Python \g<n>
            repl_py = re.sub(r"\$(\d+)", r"\\g<\1>", repl)
            count = int(edit.get("count", 0))  # 0 = replace all
            flags = re.MULTILINE
            if edit.get("ignore_case"):
                flags |= re.IGNORECASE
            text = re.sub(pattern, repl_py, text, count=count, flags=flags)
        else:
            allowed = "anchor_insert, prepend, append, replace_range, regex_replace"
            raise RuntimeError(
                f"unknown edit op: {op}; allowed: {allowed}. Use 'op' (aliases accepted: type/mode/operation).")
    return text


def _find_best_anchor_match(pattern: str, text: str, flags: int, prefer_last: bool = True):
    """
    Find the best anchor match using improved heuristics.

    For patterns like \\s*}\\s*$ that are meant to find class-ending braces,
    this function uses heuristics to choose the most semantically appropriate match:

    1. If prefer_last=True, prefer the last match (common for class-end insertions)
    2. Use indentation levels to distinguish class vs method braces
    3. Consider context to avoid matches inside strings/comments

    Args:
        pattern: Regex pattern to search for
        text: Text to search in  

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Use one of the exact allowed values: anchor_insert, prepend, append, replace_range, regex_replace.
  2. Check for typos and abbreviations — the match is exact and case-insensitive.
  3. If you need to delete text, use replace_range with text="" covering the range, or regex_replace with an empty replacement.

Example fix

// before
edits=[{"op": "insert", "anchor": "class Foo {", "text": "// new\n"}]
// after
edits=[{"op": "anchor_insert", "anchor": "class Foo {", "position": "before", "text": "// new\n"}]
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED_OPS = {"anchor_insert", "prepend", "append", "replace_range", "regex_replace"}
for i, edit in enumerate(edits):
    op = (edit.get("op") or edit.get("operation") or edit.get("type") or edit.get("mode") or "").strip().lower()
    if op and op not in ALLOWED_OPS:
        raise ValueError(f"Edit {i} has unknown op '{op}'. Allowed: {sorted(ALLOWED_OPS)}")

Type guard

ALLOWED_OPS = {"anchor_insert", "prepend", "append", "replace_range", "regex_replace"}

def edit_has_recognized_op(edit: dict) -> bool:
    op = (edit.get("op") or edit.get("operation") or edit.get("type") or edit.get("mode") or "").strip().lower()
    return op == "" or op in ALLOWED_OPS

Prevention

When it happens

Trigger: A caller uses op='insert' (should be 'anchor_insert'); op='replace' (should be 'replace_range'); op='delete' (not supported); a typo like 'apend' or 'replce'; a caller assumes an op like 'overwrite' or 'line_insert' exists.

Common situations: An LLM invents a plausible op name not in the allowed set; a caller abbreviates an op name; documentation/version mismatch where an op was renamed; a caller confuses this tool's ops with another editor's vocabulary (e.g., LSP TextDocumentEdit operations).

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/ea17c087f94758a6. Report an issue: GitHub.