{"record":{"id":"277020a0ef6d67c9","repo":"clockworklabs/SpacetimeDB","slug":"procedure-return-value-failed-to-serialize-to-bsat","errorCode":null,"errorMessage":"Procedure return value failed to serialize to BSATN","messagePattern":"Procedure return value failed to serialize to BSATN","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/client/messages.rs","lineNumber":848,"sourceCode":"            let status = match status {\n                ProcedureStatus::InternalError(msg) => ws_v1::ProcedureStatus::InternalError(msg),\n                ProcedureStatus::OutOfEnergy => ws_v1::ProcedureStatus::OutOfEnergy,\n                ProcedureStatus::Returned(val) => ws_v1::ProcedureStatus::Returned(serialize_value(val)),\n            };\n            ws_v1::ServerMessage::ProcedureResult(ws_v1::ProcedureResult {\n                status,\n                timestamp,\n                total_host_execution_duration,\n                request_id,\n            })\n        }\n\n        // Note that procedure returns are sent only to the caller, not broadcast to all subscribers,\n        // so we don't have to bother with memoizing the serialization the way we do for reducer args.\n        match protocol {\n            Protocol::Binary => ws_v1::FormatSwitch::Bsatn(convert(self, |val| {\n                bsatn::to_vec(&val)\n                    .expect(\"Procedure return value failed to serialize to BSATN\")\n                    .into()\n            })),\n            Protocol::Text => ws_v1::FormatSwitch::Json(convert(self, |val| {\n                serde_json::to_string(&SerializeWrapper(val))\n                    .expect(\"Procedure return value failed to serialize to JSON\")\n                    .into()\n            })),\n        }\n    }\n}\n","sourceCodeStart":830,"sourceCodeEnd":859,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/core/src/client/messages.rs#L830-L859","documentation":"When a procedure is called over WebSocket v1 with the binary protocol, the host serializes the return value with `bsatn::to_vec(...).expect(\"Procedure return value failed to serialize to BSATN\")` before sending it only to the caller. BSATN encoding of a well-formed SpacetimeType is essentially infallible, so this panic indicates the return type violates serialization invariants — a hand-written Serialize that errors, or a malformed/mismatched runtime type after schema changes.","triggerScenarios":"Calling a procedure whose return type fails BSATN encoding: custom Serialize impls returning errors, schema/runtime skew after changing the module without republishing, or SDK/macro version drift producing malformed types.","commonSituations":"Editing a module's return type and connecting with stale client bindings; hand-rolled Serialize impls on return values; mismatched spacetimedb crate versions between module and SDK.","solutions":["Republish the module so host and schema agree, then regenerate client bindings and reconnect.","Unit-test the return type in the module crate: `bsatn::to_vec(&sample)` should succeed before deploy.","Replace custom Serialize impls on return values with derived SpacetimeType impls.","Align SDK/module/crate versions — version drift can produce malformed types."],"exampleFix":"// before: hand-rolled Serialize on the return type can fail at runtime\nfn get_cfg(ctx: &ReducerContext) -> CustomRet { CustomRet::bad_impl() }\n\n// after: derive SpacetimeType and smoke-test encoding\n#[derive(spacetimedb::SpacetimeType, Clone)]\npub struct CustomRet { pub level: u8 }\n\n#[test]\nfn ret_bsatn_ok() { assert!(spacetimedb_lib::bsatn::to_vec(&CustomRet { level: 3 }).is_ok()); }","handlingStrategy":"type-guard","validationCode":"// Module-side test: prove every return value encodes before deploying\n#[test]\nfn procedure_returns_encode() {\n    let sample = make_sample_return();\n    assert!(spacetimedb_lib::bsatn::to_vec(&sample).is_ok());\n}","typeGuard":"fn assert_bsatn_serializable<T: spacetimedb_lib::bsatn::Serialize>() {}\n// invoke in module init: assert_bsatn_serializable::<MyRet>();","tryCatchPattern":null,"preventionTips":["Derive SpacetimeType for all procedure return types; never hand-write Serialize for them.","Add serialization smoke tests for public return shapes in CI.","Regenerate client bindings and republish after every module change before connecting."],"tags":["rust","spacetimedb","bsatn","serialization","procedure","websocket"],"backgroundTag":"bsatn-serialization-failed","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}