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

  1. Regenerate all bindings with the spacetimedb CLI version that matches the installed SDK package.
  2. If you wrote the table type by hand, make the row type derive from SpacetimeDB.Runtime.Row like generated row classes do.
  3. 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

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


AI-assisted analysis of clockworklabs/SpacetimeDB@9e0d92412f (2026-08-20). Data as JSON: /api/errors/c0995e59ea6b760c. Report an issue: GitHub.