{"record":{"id":"5bd6bf321a90379f","repo":"reflex-dev/reflex","slug":"arguments-to-event-handlers-must-be-vars-or-json-s","errorCode":null,"errorMessage":"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}.","messagePattern":"Arguments to event handlers must be Vars or JSON-serializable\\. Got (.+?) of type (.+?)\\.","errorType":"exception","errorClass":"EventHandlerTypeError","httpStatus":null,"severity":"error","filePath":"packages/reflex-base/src/reflex_base/event/__init__.py","lineNumber":669,"sourceCode":"            # Special case for file uploads. The upload arg takes its own\n            # positional slot so the remaining args stay aligned with fn_args,\n            # but its parameter name is re-derived server-side in as_event_spec.\n            if isinstance(arg, (FileUpload, UploadFilesChunk)):\n                if upload_event_spec is not None:\n                    msg = (\n                        f\"Event handler {self.fn.__name__} received multiple file \"\n                        \"upload arguments.\"\n                    )\n                    raise EventHandlerTypeError(msg)\n                upload_event_spec = arg.as_event_spec(handler=self)\n                continue\n\n            # Otherwise, convert to JSON.\n            try:\n                payload.append((Var(_js_expr=fn_arg), LiteralVar.create(arg)))\n            except TypeError as e:\n                msg = f\"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}.\"\n                raise EventHandlerTypeError(msg) from e\n\n        if upload_event_spec is not None:\n            if not payload:\n                return upload_event_spec\n            # The extra bound args share the flat payload with the synthetic\n            # upload args, so reject names that would clobber a reserved upload\n            # key (files, upload_id, extra_headers, ...).\n            payload_names = [name._js_expr for name, _ in payload]\n            reserved = {name._js_expr for name, _ in upload_event_spec.args}\n            clash = next((name for name in payload_names if name in reserved), None)\n            if clash is not None:\n                msg = (\n                    f\"Event handler {self.fn.__name__} argument {clash!r} conflicts \"\n                    \"with a reserved upload argument.\"\n                )\n                raise EventHandlerTypeError(msg)\n            # The client uploadFiles handler forwards exactly the args named here,\n            # so it never has to know the reserved upload keys.","sourceCodeStart":651,"sourceCodeEnd":687,"githubUrl":"https://github.com/reflex-dev/reflex/blob/45b8ed5ab735f8a56bbb09a42384f030eb0208e7/packages/reflex-base/src/reflex_base/event/__init__.py#L651-L687","documentation":"Event-handler arguments become part of the JSON event payload sent to the browser; anything that is not a Var or JSON-serializable cannot be represented and the LiteralVar.create call raises TypeError, which is wrapped in this EventHandlerTypeError.","triggerScenarios":"Calling State.my_event(obj) where obj is e.g. a database model, socket, PIL image, set, or custom class without a serializer.","commonSituations":"Passing ORM objects or numpy arrays from render code; storing non-serializable values and forwarding them to events; forgetting to extract primitive fields first.","solutions":["Convert the value to primitives (str/int/float/bool/list/dict) or its JSON equivalent before passing","Wrap the value in a Var if it is already a computed Var","Pass an id/key and look the object up server-side in the handler instead"],"exampleFix":"# before\nState.select(user)  # user is a SQLAlchemy model\n# after\nState.select(user.id)","handlingStrategy":"type-guard","validationCode":"import json\n\ndef is_event_serializable(v) -> bool:\n    try:\n        json.dumps(v, default=lambda o: getattr(o, '_var_type', None) or (_ for _ in ()).throw(TypeError))\n        return True\n    except TypeError:\n        return hasattr(v, '_var_type')  # it's a Var","typeGuard":"def is_event_arg(v) -> TypeGuard[object]: import json; try: json.dumps(v); return True; except TypeError: return hasattr(v, '_var_type')","tryCatchPattern":"try:\n    State.h(obj)\nexcept EventHandlerTypeError:\n    State.h(obj.id)  # fallback to identifier","preventionTips":["Only pass primitives, containers of primitives, or Vars to event calls","Convert models to dicts/ids at the boundary"],"tags":["reflex","event-handler","json-serialization"],"backgroundTag":"json-serialization-failed","analyzedSha":"45b8ed5ab735f8a56bbb09a42384f030eb0208e7","analyzedAt":"2026-08-28T19:25:27.644Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}