{"record":{"id":"b6382319e8261ddd","repo":"clockworklabs/SpacetimeDB","slug":"unknown-reducer-reducer","errorCode":null,"errorMessage":"Unknown reducer {reducer}","messagePattern":"Unknown reducer (.+?)","errorType":"exception","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"crates/codegen/src/csharp.rs","lineNumber":1246,"sourceCode":"                output,\n                \"protected override bool Dispatch(IReducerEventContext context, Reducer reducer)\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"var eventContext = (ReducerEventContext)context;\");\n                writeln!(output, \"return reducer switch {{\");\n                {\n                    indent_scope!(output);\n                    for reducer_name in\n                        iter_reducers(module, options.visibility).map(|r| r.accessor_name.deref().to_case(Case::Pascal))\n                    {\n                        writeln!(\n                            output,\n                            \"Reducer.{reducer_name} args => Reducers.Invoke{reducer_name}(eventContext, args),\"\n                        );\n                    }\n                    writeln!(\n                        output,\n                        r#\"_ => throw new ArgumentOutOfRangeException(\"Reducer\", $\"Unknown reducer {{reducer}}\")\"#\n                    );\n                }\n                writeln!(output, \"}};\");\n            });\n            writeln!(output);\n\n            writeln!(output, \"public SubscriptionBuilder SubscriptionBuilder() => new(this);\");\n            writeln!(\n                output,\n                \"public event Action<ReducerEventContext, Exception> OnUnhandledReducerError\"\n            );\n            indented_block(output, |output| {\n                writeln!(output, \"add => Reducers.InternalOnUnhandledReducerError += value;\");\n                writeln!(output, \"remove => Reducers.InternalOnUnhandledReducerError -= value;\");\n            });\n        });\n\n        vec![OutputFile {","sourceCodeStart":1228,"sourceCodeEnd":1264,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/codegen/src/csharp.rs#L1228-L1264","documentation":"The C# code generator emits a switch expression that maps incoming Reducer.{PascalName} event objects to generated Invoke{Reducer} handlers, ending with `_ => throw new ArgumentOutOfRangeException(...)`. At runtime this fires when the client receives a reducer-call event for a reducer that has no case in the generated switch — almost always because the client's generated bindings are older than the module the server is running.","triggerScenarios":"The server module is republished with new (or renamed) reducers while a client still runs bindings generated from the older schema; a long-lived client connects to a database whose module was updated; the ReducerEventArgs payload is constructed/dispatched with a name not present in the generated Reducer class.","commonSituations":"CI auto-publishes the module but the client app build skips `spacetime generate`; desktop/mobile clients that do not auto-update connecting to a freshly published module; publishing schema changes to a shared database name used by multiple client versions.","solutions":["Regenerate the C# bindings from the deployed module version (`spacetime generate --lang csharp --out-dir ... --project-path <module>`) and ship a client update","Deploy in lockstep: publish the new module only after clients that understand the new reducers have rolled out, or gate new reducers behind a version check","Verify each client connects to the database version its bindings were generated from","Audit the generated Reducer switch cases against the module's reducer list before release"],"exampleFix":"// before: module adds a reducer, client keeps old generated bindings\n#[spacereduce]\nfunction mint_token(ctx, amount) { ... }\n// old client receives Reducer.MintToken -> ArgumentOutOfRangeException\n\n// after: regenerate and rebuild the client\n// $ spacetime generate --lang csharp --out-dir ./generated --project-path ./module\n// generated switch now contains:\n// Reducer.MintToken args => Reducers.InvokeMintToken(eventContext, args),","handlingStrategy":"try-catch","validationCode":"// After generating bindings, assert the generated reducer set matches the module:\n// (run in CI against the published module schema)\n// spacetime calls list <db> --json | compare with Reducer.* cases in generated code","typeGuard":null,"tryCatchPattern":"try {\n    await conn.SubscriptionBuilder().OnApplied(...).Subscribe(...);\n} catch (ArgumentOutOfRangeException e) when (e.ParamName == \"Reducer\") {\n    // unknown reducer event: bindings are stale -> prompt regeneration/update\n    Logger.LogError($\"Stale client bindings: {e.Message}\");\n}","preventionTips":["Run `spacetime generate` in the same pipeline step that publishes the module","Pin clients to a known database/module version and version-check at connect time","Treat new reducers as a coordinated server+client release"],"tags":["csharp","codegen","reducer","bindings","version-mismatch"],"backgroundTag":"stale-generated-bindings","analyzedSha":"eddf9f5014579a50d4b67630e28b6e15cad9c4af","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}