clockworklabs/SpacetimeDB · error · Exception
Invalid row type for table {RemoteTableName}: {value.GetType
Error message
Invalid row type for table {RemoteTableName}: {value.GetType().Name} What it means
After applying a delta, Table.Apply walks the wasInserted list and invokes the internal insert handler for each value, requiring every value in the table's row cache to be an instance of SpacetimeDB.Runtime.Row. This exception means a newly inserted cache value has some other type, breaking the SDK's internal invariant. It is not triggered by bad row data, only by a wrong value type in the cache.
Source
Thrown at sdks/csharp/src/Table.cs:520
// (And we need to do it before any PostApply is called.)
// Reminder: We need to loop through the removed entries to delete them prior to inserting the new entries,
// in order to avoid keys an error with the same key already added.
foreach (var (_, value) in wasRemoved)
{
if (value is Row oldRow)
{
OnInternalDeleteHandler.Invoke(oldRow);
}
}
foreach (var (_, value) in wasInserted)
{
if (value is Row newRow)
{
OnInternalInsertHandler.Invoke(newRow);
}
else
{
throw new Exception($"Invalid row type for table {RemoteTableName}: {value.GetType().Name}");
}
}
foreach (var (_, oldValue, newValue) in wasUpdated)
{
if (oldValue is Row oldRow)
{
OnInternalDeleteHandler.Invoke(oldRow);
}
else
{
throw new Exception($"Invalid row type for table {RemoteTableName}: {oldValue.GetType().Name}");
}
if (newValue is Row newRow)
{
OnInternalInsertHandler.Invoke(newRow);
}View on GitHub (pinned to 9e0d92412f)
Solutions
- Regenerate all bindings with the spacetimedb CLI version that matches the installed SDK package.
- If you wrote the table type by hand, make the row type derive from SpacetimeDB.Runtime.Row like generated row classes do.
- If everything is generated and versions match, capture the delta/table name and file an SDK issue - the invariant should hold.
Example fix
// before: hand-written table with a plain POCO row type
class PlayerRow { public ulong Id; }
// after: row types must derive from Row (as generated code does)
using SpacetimeDB.Runtime;
class PlayerRow : Row { public ulong Id; } Defensive patterns
Strategy: type-guard
Validate before calling
foreach (var t in typeof(Program).Assembly.GetTypes())
{
if (t.GetCustomAttributes().Any(a => a.GetType().Name == "TableAttribute"))
{
// row types used by table handles must be Row subclasses
}
} Type guard
static bool IsRowType(Type t) => typeof(SpacetimeDB.Runtime.Row).IsAssignableFrom(t);
Try / catch
conn.SubscriptionBuilder().OnError(ex => Log.Error($"row invariant broken: {ex.Message}")); Prevention
- Always generate table/row types with the CLI matching your SDK version.
- In custom table code, derive row types from SpacetimeDB.Runtime.Row.
- Treat this exception as a bug report signal - do not swallow it.
When it happens
Trigger: The table's cache value type is not a Row-derived class: hand-written table handles or a custom IRemoteTableHandle implementation, or generated bindings from an SDK generation that used non-Row row types paired with a newer SDK runtime.
Common situations: Upgrading the SpacetimeDB C# SDK major version while keeping bindings generated by an older CLI; implementing a custom table abstraction instead of using generated table handles.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Invalid row type for table {RemoteTableName}: {oldValue.GetT
- Invalid row type for table {RemoteTableName}: {newValue.GetT
- Unsupported .NET SDK version: {major}. SpacetimeDB requires
- Token not initialized. Call AuthToken.Init() first.
- While table `{RemoteTableName}` was applying: {deltaString}
AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20).
Data as JSON: /api/errors/c0995e59ea6b760c.
Report an issue: GitHub.