PrefectHQ/fastmcp · error · TypeError

Elicitation schema field '{prop_name}' contains a reference

Error message

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.

What it means

Elicitation schemas may contain $ref pointers only when they resolve to enum or primitive-typed definitions. If a property's $ref cannot be resolved (missing $defs entry, ref to a nested object/array, or a ref the validator can't trace), the validator rejects it defensively rather than sending an unverifiable schema to the client.

Source

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

        if "enum" in prop_schema:
            continue  # enum fields are allowed regardless of type

        # Handle references to definitions (like Enum types)
        if "$ref" in prop_schema:
            # Get the referenced definition
            ref_path = prop_schema["$ref"]
            if ref_path.startswith("#/$defs/"):
                def_name = ref_path[8:]  # Remove "#/$defs/" prefix
                ref_def = schema.get("$defs", {}).get(def_name, {})
                # If the referenced definition has an enum, it's allowed
                if "enum" in ref_def:
                    continue
                # If the referenced definition has a type that's allowed, it's allowed
                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

View on GitHub (pinned to 1f02114297)

Solutions

  1. Ensure every $ref resolves to a $defs entry with an allowed primitive type or an enum definition
  2. Inline the enum/primitive directly in the property instead of using $ref
  3. Use FastMCP's schema generation (Pydantic models / shorthand forms) rather than hand-built $defs — its generator inlines enums automatically
  4. Run compress_schema or equivalent before validation if composing schemas programmatically

Example fix

// before
{"properties": {"color": {"$ref": "#/$defs/Missing"}}}
// after
{"properties": {"color": {"type": "string", "enum": ["red", "blue"]}}}
Defensive patterns

Strategy: validation

Validate before calling

def check_refs(schema: dict) -> None:
    defs = schema.get("$defs", {})
    for prop in schema.get("properties", {}).values():
        ref = prop.get("$ref")
        if not ref:
            continue
        name = ref.rsplit("/", 1)[-1]
        target = defs.get(name, {})
        if target.get("type") not in {"string", "number", "integer", "boolean"} and "enum" not in target:
            raise TypeError(f"ref {ref} must resolve to a primitive or enum definition")

Type guard

def ref_is_resolvable_primitive(schema: dict, ref_path: str) -> bool:
    if not ref_path.startswith("#/$defs/"):
        return False
    target = schema.get("$defs", {}).get(ref_path.rsplit("/", 1)[-1], {})
    return target.get("type") in {"string", "number", "integer", "boolean"} or "enum" in target

Try / catch

try:
    schema = get_elicitation_schema(response_type)
except TypeError as e:
    if "could not be validated" in str(e) and "$ref" in str(e) or "reference" in str(e):
        logger.error("inline your enum/primitive instead of using $ref")
        return None
    raise

Prevention

When it happens

Trigger: A property schema like {"$ref": "#/$defs/Foo"} where $defs lacks Foo, or where the resolved definition has no 'type' and no enum/const, during get_elicitation_schema validation.

Common situations: Manually composing schemas with shared $defs; post-processing/compressing Pydantic schemas so refs dangle; referencing a BaseModel (object) instead of an Enum; schema tooling rewriting ref paths.

Related errors


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