{"record":{"id":"3023879cd935246c","repo":"PrefectHQ/fastmcp","slug":"elicitation-schema-field-prop-name-has-union-t","errorCode":null,"errorMessage":"Elicitation schema field '{prop_name}' has union type '{union_type}' which is not a primitive type. Only {ALLOWED_TYPES} are allowed in elicitation schemas.","messagePattern":"Elicitation schema field '(.+?)' has union type '(.+?)' which is not a primitive type\\. Only (.+?) are allowed in elicitation schemas\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/elicitation.py","lineNumber":471,"sourceCode":"                ref_type = ref_def.get(\"type\")\n                if ref_type in ALLOWED_TYPES:\n                    continue\n            # If we can't determine what the ref points to, reject it for safety\n            raise TypeError(\n                f\"Elicitation schema field '{prop_name}' contains a reference '{ref_path}' \"\n                \"that could not be validated. Only references to enum types or primitive types are allowed.\"\n            )\n\n        # Handle union types (oneOf/anyOf)\n        if \"oneOf\" in prop_schema or \"anyOf\" in prop_schema:\n            union_schemas = prop_schema.get(\"oneOf\", []) + prop_schema.get(\"anyOf\", [])\n            for union_schema in union_schemas:\n                # Allow const and enum in unions\n                if \"const\" in union_schema or \"enum\" in union_schema:\n                    continue\n                union_type = union_schema.get(\"type\")\n                if union_type not in ALLOWED_TYPES:\n                    raise TypeError(\n                        f\"Elicitation schema field '{prop_name}' has union type '{union_type}' which is not \"\n                        f\"a primitive type. Only {ALLOWED_TYPES} are allowed in elicitation schemas.\"\n                    )\n            continue\n\n        # Check for arrays before checking primitive types\n        if prop_type == \"array\":\n            items_schema = prop_schema.get(\"items\", {})\n            if items_schema.get(\"type\") == \"object\":\n                raise TypeError(\n                    f\"Elicitation schema field '{prop_name}' is an array of objects, but arrays of objects are not allowed. \"\n                    \"Elicitation schemas must be flat objects with primitive properties only.\"\n                )\n\n            # Allow arrays with enum patterns (for multi-select)\n            if \"enum\" in items_schema:\n                continue  # Allowed: {\"type\": \"array\", \"items\": {\"enum\": [...]}}\n","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/elicitation.py#L453-L489","documentation":"Within a oneOf/anyOf union property, every branch must be an allowed primitive type (string, number, integer, boolean) or a const/enum branch. A union branch with a non-primitive type (object, array, or missing type) is rejected because MCP elicitation clients can only render primitive unions.","triggerScenarios":"A property like {\"anyOf\": [{\"type\": \"string\"}, {\"type\": \"object\"}]} or {\"oneOf\": [{\"type\": \"array\", ...}]} passed through get_elicitation_schema validation.","commonSituations":"Modeling optional/nullable fields with unions containing null (type 'null' is not in ALLOWED_TYPES); using Union[ModelA, ModelB]; generating schemas from types like Union[str, list[str]].","solutions":["Restrict unions to primitive branches, e.g. Union[str, int] or Literal values","Replace Optional[T] where T is complex with a plain primitive or a default value — note type 'null' is not allowed, so avoid Optional[X] unions that emit a null branch","Model complex alternatives as separate elicitation fields instead of one union property"],"exampleFix":"// before\nfield: Union[str, dict]  # anyOf with object branch\n// after\nfield: Union[str, int]  # primitive branches only","handlingStrategy":"validation","validationCode":"ALLOWED = {\"string\", \"number\", \"integer\", \"boolean\"}\ndef check_unions(schema: dict) -> None:\n    for prop in schema.get(\"properties\", {}).values():\n        branches = prop.get(\"anyOf\") or prop.get(\"oneOf\") or []\n        for b in branches:\n            if \"const\" in b or \"enum\" in b:\n                continue\n            if b.get(\"type\") not in ALLOWED:\n                raise TypeError(f\"union branch type {b.get('type')!r} not allowed in elicitation\")","typeGuard":"def is_primitive_union(prop_schema: dict) -> bool:\n    branches = prop_schema.get(\"anyOf\") or prop_schema.get(\"oneOf\") or []\n    return all(\n        \"const\" in b or \"enum\" in b or b.get(\"type\") in {\"string\", \"number\", \"integer\", \"boolean\"}\n        for b in branches\n    )","tryCatchPattern":"try:\n    schema = get_elicitation_schema(response_type)\nexcept TypeError as e:\n    if \"union type\" in str(e) and \"primitive\" in str(e):\n        logger.error(\"use only primitive (or const/enum) union branches in elicitation models\")\n        return None\n    raise","preventionTips":["Keep unions to primitives: Union[str, int], Literal values, Enum values","Avoid Optional[ComplexType] unions — 'null' and object branches are rejected","Model each complex alternative as its own flat field instead of a union","Check generated JSON schemas for anyOf/oneOf branches in CI"],"tags":["elicitation","json-schema","union-types","fastmcp"],"backgroundTag":"schema-validation-failed","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}