{"record":{"id":"7c44cd0a8762429b","repo":"PrefectHQ/fastmcp","slug":"value-for-state-key-key-r-is-not-serializable-u","errorCode":null,"errorMessage":"Value for state key {key!r} is not serializable. Use set_state({key!r}, value, serializable=False) to store non-serializable values. Note: non-serializable state is request-scoped and will not persist across requests.","messagePattern":"Value for state key (.+?) is not serializable\\. Use set_state\\((.+?), value, serializable=False\\) to store non-serializable values\\. Note: non-serializable state is request-scoped and will not persist across requests\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/context.py","lineNumber":1143,"sourceCode":"        The key is automatically prefixed with the session identifier.\n        \"\"\"\n        prefixed_key = self._make_state_key(key)\n        if not serializable:\n            self._request_state[prefixed_key] = value\n            return\n        # Clear any request-scoped shadow so the session value is visible\n        self._request_state.pop(prefixed_key, None)\n        try:\n            await self.fastmcp._state_store.put(\n                key=prefixed_key,\n                value=StateValue(value=value),\n                ttl=self._STATE_TTL_SECONDS,\n            )\n        except ValueError as e:\n            # Pydantic raises PydanticSerializationError (a ValueError) and the\n            # message carries \"serialize\". Other ValueErrors propagate unchanged.\n            if \"serialize\" in str(e).lower():\n                raise TypeError(\n                    f\"Value for state key {key!r} is not serializable. \"\n                    f\"Use set_state({key!r}, value, serializable=False) to store \"\n                    f\"non-serializable values. Note: non-serializable state is \"\n                    f\"request-scoped and will not persist across requests.\"\n                ) from e\n            raise\n        except Exception as e:\n            # Import the optional storage implementation only on its error path,\n            # rather than adding the key_value package to every server startup.\n            from key_value.aio.errors import SerializationError\n\n            if not isinstance(e, SerializationError):\n                raise\n            raise TypeError(\n                f\"Value for state key {key!r} is not serializable. \"\n                f\"Use set_state({key!r}, value, serializable=False) to store \"\n                f\"non-serializable values. Note: non-serializable state is \"\n                f\"request-scoped and will not persist across requests.\"","sourceCodeStart":1125,"sourceCodeEnd":1161,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/context.py#L1125-L1161","documentation":"Context.set_state() serializes values (via pydantic) before storing them in the key-value store. When pydantic raises a serialization ValueError, set_state converts it into this TypeError telling you to pass serializable=False. Non-serializable state is still allowed but is request-scoped and will not persist across requests.","triggerScenarios":"ctx.set_state(key, value) where value cannot be pydantic-serialized (e.g. an open file handle, a lock, a custom object without serialization support) and serializable is not set to False.","commonSituations":"Storing database connections, asyncio primitives, or dataclass instances in server state; refactoring code that previously kept such objects in module globals into Context state.","solutions":["Pass serializable=False if the value only needs to live for the current request: ctx.set_state(key, value, serializable=False).","Otherwise store a serializable representation (dict of primitives, JSON string) and reconstruct the object on read.","For objects needing custom serialization, add a model serializer or convert to a supported type before storing."],"exampleFix":"// before\nawait ctx.set_state(\"engine\", engine)\n// after\nawait ctx.set_state(\"engine\", engine, serializable=False)  # request-scoped\n// or persist data instead:\nawait ctx.set_state(\"engine_dsn\", engine.url) ","handlingStrategy":"validation","validationCode":"import pydantic\ntry:\n    pydantic.TypeAdapter(Any).dump_python(value)\n    serializable = True\nexcept Exception:\n    serializable = False\nawait ctx.set_state(key, value, serializable=not serializable and needs_request_scope)","typeGuard":null,"tryCatchPattern":"try:\n    await ctx.set_state(key, value)\nexcept TypeError as e:\n    if \"not serializable\" in str(e):\n        await ctx.set_state(key, value, serializable=False)\n    else:\n        raise","preventionTips":["Store only JSON-compatible values in persistent state","Use serializable=False knowingly for request-scoped objects","Add a unit test asserting every state key you use round-trips"],"tags":["state","serialization","pydantic"],"backgroundTag":"state-value-not-serializable","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}