{"record":{"id":"64db8cdf5337cd94","repo":"CoplayDev/unity-mcp","slug":"op-is-required-allowed-allowed-use-op-alia","errorCode":null,"errorMessage":"op is required; allowed: {allowed}. Use 'op' (aliases accepted: type/mode/operation).","messagePattern":"op is required; allowed: (.+?)\\. Use 'op' \\(aliases accepted: type/mode/operation\\)\\.","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Server/src/services/tools/script_apply_edits.py","lineNumber":393,"sourceCode":"    return False\n\n\nasync def _apply_edits_locally(original_text: str, edits: list[dict[str, Any]]) -> str:\n    text = original_text\n    for edit in edits or []:\n        op = (\n            (edit.get(\"op\")\n             or edit.get(\"operation\")\n             or edit.get(\"type\")\n             or edit.get(\"mode\")\n             or \"\")\n            .strip()\n            .lower()\n        )\n\n        if not op:\n            allowed = \"anchor_insert, prepend, append, replace_range, regex_replace\"\n            raise RuntimeError(\n                f\"op is required; allowed: {allowed}. Use 'op' (aliases accepted: type/mode/operation).\"\n            )\n\n        if op == \"prepend\":\n            prepend_text = edit.get(\"text\", \"\")\n            text = (prepend_text if prepend_text.endswith(\n                \"\\n\") else prepend_text + \"\\n\") + text\n        elif op == \"append\":\n            append_text = edit.get(\"text\", \"\")\n            if not text.endswith(\"\\n\"):\n                text += \"\\n\"\n            text += append_text\n            if not text.endswith(\"\\n\"):\n                text += \"\\n\"\n        elif op == \"anchor_insert\":\n            anchor = edit.get(\"anchor\", \"\")\n            position = (edit.get(\"position\") or \"before\").lower()\n            insert_text = edit.get(\"text\", \"\")","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/Server/src/services/tools/script_apply_edits.py#L375-L411","documentation":"In script_apply_edits' local edit applier (_apply_edits_locally), each edit dict must specify an operation. The code reads 'op' with aliases 'operation', 'type', 'mode' (first non-empty wins, case-insensitive, trimmed). If all are absent or empty, this RuntimeError fires listing the five allowed ops: anchor_insert, prepend, append, replace_range, regex_replace. This is the first validation per edit — without an op the applier cannot decide what to do.","triggerScenarios":"An edit dict is missing the op key entirely: {\"text\": \"...\"}; all alias keys are empty strings; the caller uses an unrecognized key like 'action' or 'edit_type' instead of op/operation/type/mode; a JSON payload where op was stripped by a schema filter.","commonSituations":"An LLM omits the op field assuming a default; a caller uses 'action' (common in other APIs) instead of 'op'; a programmatic edit builder forgets to set the operation; a serialization layer drops unknown keys.","solutions":["Add \"op\": \"<one of anchor_insert|prepend|append|replace_range|regex_replace>\" to each edit dict.","If you used a different key name, switch to 'op' (or an accepted alias: operation, type, mode).","Validate every edit has a non-empty op before calling script_apply_edits."],"exampleFix":"// before\nedits=[{\"text\": \"new code\\n\"}]\n// after\nedits=[{\"op\": \"prepend\", \"text\": \"new code\\n\"}]","handlingStrategy":"validation","validationCode":"ALLOWED_OPS = {\"anchor_insert\", \"prepend\", \"append\", \"replace_range\", \"regex_replace\"}\nfor i, edit in enumerate(edits):\n    op = (edit.get(\"op\") or edit.get(\"operation\") or edit.get(\"type\") or edit.get(\"mode\") or \"\").strip().lower()\n    if not op:\n        raise ValueError(f\"Edit {i} is missing 'op'. Allowed: {ALLOWED_OPS}\")\n    if op not in ALLOWED_OPS:\n        raise ValueError(f\"Edit {i} has unknown op '{op}'. Allowed: {ALLOWED_OPS}\")","typeGuard":"ALLOWED_OPS = {\"anchor_insert\", \"prepend\", \"append\", \"replace_range\", \"regex_replace\"}\n\ndef edit_has_valid_op(edit: dict) -> bool:\n    op = (edit.get(\"op\") or edit.get(\"operation\") or edit.get(\"type\") or edit.get(\"mode\") or \"\").strip().lower()\n    return op in ALLOWED_OPS","tryCatchPattern":null,"preventionTips":["Always include 'op' in every edit dict with one of the five allowed values.","Use the accepted alias keys (operation/type/mode) only if 'op' is unavailable — prefer 'op'.","Validate all edits have a recognized op before calling script_apply_edits."],"tags":["validation","script-edit","edit-op","mcp-tool"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}