PrefectHQ/fastmcp · error · TypeError

Elicitation schema field '{prop_name}' has union type '{unio

Error message

Elicitation schema field '{prop_name}' has union type '{union_type}' which is not a primitive type. Only {ALLOWED_TYPES} are allowed in elicitation schemas.

What it means

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.

Source

Thrown at fastmcp_slim/fastmcp/server/elicitation.py:471

                ref_type = ref_def.get("type")
                if ref_type in ALLOWED_TYPES:
                    continue
            # If we can't determine what the ref points to, reject it for safety
            raise TypeError(
                f"Elicitation schema field '{prop_name}' contains a reference '{ref_path}' "
                "that could not be validated. Only references to enum types or primitive types are allowed."
            )

        # Handle union types (oneOf/anyOf)
        if "oneOf" in prop_schema or "anyOf" in prop_schema:
            union_schemas = prop_schema.get("oneOf", []) + prop_schema.get("anyOf", [])
            for union_schema in union_schemas:
                # Allow const and enum in unions
                if "const" in union_schema or "enum" in union_schema:
                    continue
                union_type = union_schema.get("type")
                if union_type not in ALLOWED_TYPES:
                    raise TypeError(
                        f"Elicitation schema field '{prop_name}' has union type '{union_type}' which is not "
                        f"a primitive type. Only {ALLOWED_TYPES} are allowed in elicitation schemas."
                    )
            continue

        # Check for arrays before checking primitive types
        if prop_type == "array":
            items_schema = prop_schema.get("items", {})
            if items_schema.get("type") == "object":
                raise TypeError(
                    f"Elicitation schema field '{prop_name}' is an array of objects, but arrays of objects are not allowed. "
                    "Elicitation schemas must be flat objects with primitive properties only."
                )

            # Allow arrays with enum patterns (for multi-select)
            if "enum" in items_schema:
                continue  # Allowed: {"type": "array", "items": {"enum": [...]}}

View on GitHub (pinned to 1f02114297)

Solutions

  1. Restrict unions to primitive branches, e.g. Union[str, int] or Literal values
  2. 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
  3. Model complex alternatives as separate elicitation fields instead of one union property

Example fix

// before
field: Union[str, dict]  # anyOf with object branch
// after
field: Union[str, int]  # primitive branches only
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {"string", "number", "integer", "boolean"}
def check_unions(schema: dict) -> None:
    for prop in schema.get("properties", {}).values():
        branches = prop.get("anyOf") or prop.get("oneOf") or []
        for b in branches:
            if "const" in b or "enum" in b:
                continue
            if b.get("type") not in ALLOWED:
                raise TypeError(f"union branch type {b.get('type')!r} not allowed in elicitation")

Type guard

def is_primitive_union(prop_schema: dict) -> bool:
    branches = prop_schema.get("anyOf") or prop_schema.get("oneOf") or []
    return all(
        "const" in b or "enum" in b or b.get("type") in {"string", "number", "integer", "boolean"}
        for b in branches
    )

Try / catch

try:
    schema = get_elicitation_schema(response_type)
except TypeError as e:
    if "union type" in str(e) and "primitive" in str(e):
        logger.error("use only primitive (or const/enum) union branches in elicitation models")
        return None
    raise

Prevention

When it happens

Trigger: A property like {"anyOf": [{"type": "string"}, {"type": "object"}]} or {"oneOf": [{"type": "array", ...}]} passed through get_elicitation_schema validation.

Common situations: 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]].

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/3023879cd935246c. Report an issue: GitHub.