{"record":{"id":"1cc425c34c6388cb","repo":"PrefectHQ/fastmcp","slug":"defs-collision-for-def-name-an-argtransform","errorCode":null,"errorMessage":"$defs collision for '{def_name}': an ArgTransform introduces a definition with the same name as an existing $defs entry but a different schema. Rename one of the colliding types to avoid the conflict.","messagePattern":"\\$defs collision for '(.+?)': an ArgTransform introduces a definition with the same name as an existing \\$defs entry but a different schema\\. Rename one of the colliding types to avoid the conflict\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/tools/tool_transform.py","lineNumber":707,"sourceCode":"                transform,\n                old_name in parent_required,\n            )\n\n            if transform_result:\n                new_name, new_schema, is_required, extracted_defs = transform_result\n                new_props[new_name] = new_schema\n                new_to_old[new_name] = old_name\n                if is_required:\n                    new_required.add(new_name)\n                # Hoist any $defs introduced by an ArgTransform(type=...) so that\n                # $ref values like \"#/$defs/...\" resolve at the schema root.\n                # Fail loudly on key collisions whose schemas differ, since\n                # silently overwriting would point earlier $ref values at the\n                # wrong type. Identical re-introductions are a no-op.\n                for def_name, def_schema in extracted_defs.items():\n                    existing = parent_defs.get(def_name)\n                    if existing is not None and existing != def_schema:\n                        raise ValueError(\n                            f\"$defs collision for '{def_name}': an ArgTransform \"\n                            f\"introduces a definition with the same name as an \"\n                            f\"existing $defs entry but a different schema. \"\n                            f\"Rename one of the colliding types to avoid the \"\n                            f\"conflict.\"\n                        )\n                    parent_defs[def_name] = def_schema\n\n        schema = {\n            \"type\": \"object\",\n            \"properties\": new_props,\n            # Iterate props (not the set) for deterministic ordering\n            \"required\": [p for p in new_props if p in new_required],\n            \"additionalProperties\": False,\n        }\n\n        if parent_defs:\n            schema[\"$defs\"] = parent_defs","sourceCodeStart":689,"sourceCodeEnd":725,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/tools/tool_transform.py#L689-L725","documentation":"This error is raised when a tool-transform ArgTransform extracts nested argument schemas whose $defs contain a definition name that already exists in the parent tool's $defs but with a different schema. Since earlier $ref values point at definitions by name, silently overwriting would corrupt those references, so the transform fails loudly instead.","triggerScenarios":"Calling an_tool() where the child tool's extracted argument schemas introduce a $defs entry with the same name as an existing parent $defs entry but a different schema body.","commonSituations":"Two tools composed via from_tool that each define a Pydantic model (or nested schema) with the same class name but different fields; refactoring one model's fields without renaming it; generating tools from separate modules that coincidentally share type names.","solutions":["Rename one of the colliding Pydantic models/types so each $defs name is unique","Use ArgTransform to drop or restructure the nested argument that pulls in the colliding definition","Flatten or inline the nested schema of one tool to avoid emitting a named definition"],"exampleFix":"// before\nclass Filter(BaseModel):\n    limit: int  # collides with parent Filter\n\n// after\nclass ToolBFilter(BaseModel):\n    limit: int","handlingStrategy":"validation","validationCode":"from fastmcp.tools.tool_transform import ArgTransform\n# After building the transform, inspect the tool's schema $defs for duplicate names:\nimport json\nschema = json.dumps(transformed_tool.parameters)\nassert schema.count('\"YourModelName\"') <= 1, \"rename colliding model\"","typeGuard":null,"tryCatchPattern":"try:\n    transformed = FastMCPTool.from_tool(tool, transform=...)\nexcept ValueError as e:\n    if \"$defs collision\" in str(e):\n        # rename the colliding type and retry\n        ...\n    raise","preventionTips":["Prefix nested model class names per tool (ToolAFilter, ToolBFilter)","Check $defs names after composing tools in tests","Avoid reusing generic names like Filter, Config, Options across tools"],"tags":["python","schema","tool-transform","json-schema"],"backgroundTag":"json-schema-definition-collision","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}