{"record":{"id":"e3241160a17362b1","repo":"microsoft/semantic-kernel","slug":"duplicate-operationid-operationid-path-pa","errorCode":null,"errorMessage":"Duplicate operationId: '{operationId}', path: '{path}', method: '{method}'","messagePattern":"Duplicate operationId: '(.+?)', path: '(.+?)', method: '(.+?)'","errorType":"exception","errorClass":"PluginInitializationError","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py","lineNumber":260,"sourceCode":"                server_entry = {\n                    \"url\": server.get(\"url\", \"/\"),\n                    \"variables\": server.get(\"variables\", {}),\n                    \"description\": server.get(\"description\", \"\"),\n                }\n                server_urls.append(server_entry)\n        else:\n            # Default server if none specified\n            server_urls = [{\"url\": \"/\", \"variables\": {}, \"description\": \"\"}]\n\n        for path, methods in paths.items():\n            for method, details in methods.items():\n                request_method = method.lower()\n                # Validate that operationId exists\n                if \"operationId\" not in details:\n                    raise PluginInitializationError(f\"operationId missing, path: '{path}', method: '{method}'\")\n                operationId = details[\"operationId\"]\n                if operationId in unique_operation_ids_registered:\n                    raise PluginInitializationError(\n                        f\"Duplicate operationId: '{operationId}', path: '{path}', method: '{method}'\"\n                    )\n                unique_operation_ids_registered.add(operationId)\n\n                summary = details.get(\"summary\", None)\n                description = details.get(\"description\", None)\n\n                # Exclude operations whose path would resolve to a different effective request target\n                # than the one offered to the selection predicate: a dot-segment (encoded or literal)\n                # or a non-relative (absolute / authority-changing) path. Excluding them here keeps\n                # operation selection and request construction on one canonical target so such a path\n                # cannot bypass an include/exclude operation-selection filter.\n                if RestApiOperation._contains_dot_segment(path) or RestApiOperation._is_non_relative_path(path):\n                    logger.warning(\n                        f\"Skipping operation {operationId} at path '{path}' because it does not resolve to a \"\n                        f\"relative path on the configured server.\"\n                    )\n                    continue","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py#L242-L278","documentation":"The parser tracks every `operationId` it registers in `unique_operation_ids_registered`; if the same id appears twice it raises `PluginInitializationError` naming the id, path, and method. `operationId` doubles as the kernel function name, so duplicates would collide.","triggerScenarios":"Two operations (different path/method, or aliased) sharing the same `operationId` string. Also possible after spec deduplication/merging that left duplicate ids.","commonSituations":"Copy-pasted operations; specs merged from multiple teams; aliases for the same endpoint; tools that auto-generate ids by method only (so `GET /a` and `GET /b` collide).","solutions":["Rename one of the duplicate `operationId`s to make it unique.","Use a linter (Spectral `operation-operationId-unique`) to find all collisions.","If merging specs, run a dedup pass that namespaces ids (e.g. prefix with the tag).","Pre-process the spec to auto-disambiguate duplicates as `{operationId}_{method}_{path}`."],"exampleFix":"# before\npaths:\n  /pets:\n    get: { operationId: list, ... }\n  /owners:\n    get: { operationId: list, ... }   # raises 1495\n\n# after\npaths:\n  /pets:\n    get: { operationId: listPets, ... }\n  /owners:\n    get: { operationId: listOwners, ... }","handlingStrategy":"validation","validationCode":"from collections import Counter\n\ndef duplicate_operation_ids(spec) -> list[str]:\n    ids = []\n    for path, methods in spec.get(\"paths\", {}).items():\n        for method, d in methods.items():\n            oid = (d or {}).get(\"operationId\")\n            if oid:\n                ids.append(oid)\n    return [oid for oid, n in Counter(ids).items() if n > 1]\n\ndupes = duplicate_operation_ids(spec)\nassert not dupes, dupes","typeGuard":"def operation_ids_are_unique(spec) -> bool:\n    return not duplicate_operation_ids(spec)","tryCatchPattern":"from semantic_kernel.exceptions import PluginInitializationError\n\ntry:\n    kernel.add_openapi_plugin(plugin_name=\"x\", openapi_parsed_spec=spec)\nexcept PluginInitializationError as e:\n    if \"Duplicate operationId\" in str(e):\n        # disambiguate ids, then retry\n        raise\n    raise","preventionTips":["Lint specs with Spectral `operation-operationId-unique`.","Namespace ids by tag when merging specs.","Generate ids deterministically from method+path.","Review duplicate-id reports before publishing specs."],"tags":["openapi-plugin","spec-validation","semantic-kernel"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}