clockworklabs/SpacetimeDB · error · InvalidOperationException
Invalid tag value, this state should be unreachable.
Error message
Invalid tag value, this state should be unreachable.
What it means
For tagged/variant types, the BSATN C# generator emits a read function shaped as `reader.ReadByte() switch { 0 => new VariantA(...), 1 => new VariantB(...), _ => throw new InvalidOperationException("Invalid tag value, this state should be unreachable.") }`. The fallback fires when the byte stream contains a variant tag with no generated case — in practice this means the payload was encoded by a module whose variant layout differs from the client's generated bindings, or the buffer is truncated/misaligned.
Solutions
- Regenerate the C# bindings from the exact module version the client connects to and redeploy
- Treat variant/enum layout changes as breaking changes: migrate clients before servers, or publish under a new database name
- If hand-decoding, verify tag values and byte offsets against the module schema
- When versions are confirmed in sync, log raw bytes on failure to check for truncated payloads
Example fix
# before: server reorders a tagged enum's variants; client keeps old generated reader # tag byte 2 no longer matches any case -> InvalidOperationException # after: regenerate from the deployed module and rebuild the client $ spacetime generate --lang csharp --out-dir ./generated --project-path ./server $ dotnet build
Defensive patterns
Strategy: try-catch
Validate before calling
// Round-trip test against server-encoded fixtures in CI:
// var bytes = LoadFixture("player_event.bsatn");
// ReducerEventArgs.Read(new BinaryReader(new MemoryStream(bytes))); // must not throw
// Regenerate fixtures whenever the module schema changes. Try / catch
try {
var evt = ReadEvent(reader);
} catch (InvalidOperationException e) when (e.Message.Contains("Invalid tag value")) {
// stale bindings or corrupt frame: log, reconnect, and prompt the user to update
Logger.LogError("BSATN tag mismatch — regenerate client bindings");
} Prevention
- Regenerate bindings in the same CI step that publishes the module
- Treat variant/enum layout changes as breaking releases
- Maintain round-trip deserialization tests with server-produced fixtures
When it happens
Trigger: Deserializing a BSATN payload whose tag byte does not match any generated case: variants added, removed, or reordered server-side while the client runs older generated code; manually crafted BSATN fixtures with wrong tag values; truncated or offset-corrupted byte buffers.
Common situations: Module republished with changed enum/variant layout while clients keep stale assemblies; test harnesses hand-encoding BSATN; partial writes from a flaky websocket being fed to the parser.
Related errors
- Operation is not valid due to the current state of the…
- Tag is out of range of enum
- Unknown reducer
- Unrecognised extra bytes in the
- Unrecognized extra bytes while decoding BSATN value
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/5dd8639277d5e360.
Report an issue: GitHub.
Appendix: source
Thrown at crates/bindings-csharp/BSATN.Codegen/Type.cs:654
{
public override string ToString() =>
$"{{m.Name}}({ SpacetimeDB.BSATN.StringUtil.GenericToString({{m.Identifier}}_) })";
}
"""
)
)
);
read = $$"""
return reader.ReadByte() switch {
{{string.Join(
"\n ",
bsatnDecls.Select((m, i) =>
$"{i} => new {m.Identifier}({m.Identifier}{TypeUse.BsatnFieldSuffix}.Read(reader)),"
)
)}}
_ => throw new System.InvalidOperationException("Invalid tag value, this state should be unreachable.")
};
""";
write = $$"""
switch (value) {
{{string.Join(
"\n",
bsatnDecls.Select((m, i) => $"""
case {m.Identifier}(var inner):
writer.Write((byte){i});
{m.Identifier}{TypeUse.BsatnFieldSuffix}.Write(writer, inner);
break;
"""))}}
}
""";
getHashCode = $$"""
switch (this) {View on GitHub (pinned to 6dee26c6ef)