PrefectHQ/fastmcp · error · ValueError

Elicitation responses must be serializable as a JSON object

Error message

Elicitation responses must be serializable as a JSON object (dict). Received: {result.content!r}

What it means

MCP elicitation responses must be a JSON-serializable dict (object). When an elicitation handler returns a bare scalar (str/int/float/bool) for a schema that is not the special single-`value`-property form case, FastMCP raises this ValueError because it cannot wrap the content into an MCP ElicitResult. The repr of the offending content is included.

Source

Thrown at fastmcp_slim/fastmcp/client/elicitation.py:78

                params.message,
                response_type,
                params,
                context,  # ty: ignore[invalid-argument-type]
            )
            # if the user returns data, we assume they've accepted the elicitation
            if not isinstance(result, ElicitResult):
                result = ElicitResult(action="accept", content=result)
            content = to_jsonable_python(result.content)
            if not isinstance(content, dict | None):
                # Auto-wrap scalar values for ScalarElicitationType schemas
                # (single "value" property). This lets handlers return T directly
                # for ctx.elicit("msg", str/int/float/bool).
                if isinstance(params, ElicitRequestFormParams) and set(
                    params.requested_schema.get("properties", {}).keys()
                ) == {"value"}:
                    content = {"value": content}
                else:
                    raise ValueError(
                        "Elicitation responses must be serializable as a JSON object (dict). Received: "
                        f"{result.content!r}"
                    )
            return MCPElicitResult(
                _meta=result.meta,  # type: ignore[call-arg]  # _meta is Pydantic alias for meta field
                action=result.action,
                content=content,
            )

        except Exception as e:
            return mcp_types.ErrorData(
                code=mcp_types.INTERNAL_ERROR,
                message=str(e),
            )

    return _elicitation_handler

View on GitHub (pinned to 1f02114297)

Solutions

  1. Return a dict whose keys match the requested_schema properties, e.g. {"confirm": True}
  2. If the schema is the single-`value` form case, return the bare scalar and it is auto-wrapped
  3. Inspect params.requested_schema in the handler to build the correct response shape

Example fix

// before
def handler(message, params):
    return "yes"  # ValueError

// after
def handler(message, params):
    return {"value": "yes"}  # or keys matching requested_schema properties
Defensive patterns

Strategy: validation

Validate before calling

def valid_elicitation_response(content, params) -> bool:
    if isinstance(content, dict):
        return True
    props = set(params.requested_schema.get("properties", {}).keys())
    return props == {"value"} and isinstance(content, (str, int, float, bool))

Type guard

def is_json_object(content: object) -> TypeGuard[dict]:
    return isinstance(content, dict)

Try / catch

try:
    ...  # handler runs during elicitation
except ValueError as e:
    if "serializable as a JSON object" in str(e):
        logger.error("elicitation handler must return a dict: %r", e)
    raise

Prevention

When it happens

Trigger: An `elicitation_handler` returning e.g. `"yes"` or `42` while the request's requested_schema has properties other than a single `value` property.

Common situations: Handlers written for simple string elicitations reused on structured schemas; returning raw user input without wrapping it in the schema's property names.

Related errors


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