clockworklabs/SpacetimeDB · error · Exception

Invalid row type for table {RemoteTableName}: {newValue.GetT

Error message

Invalid row type for table {RemoteTableName}: {newValue.GetType().Name}

What it means

In the wasUpdated loop of Table.Apply, the NEW value for each updated key must be a Row so the internal insert handler can fire for it. This variant means the value produced for the post-update row is not a Row instance. It points at how updated values are materialized into the cache (row deserialization/type mapping), not at your reducer logic.

Source

Thrown at sdks/csharp/src/Table.cs:541

            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);
                }
                else
                {
                    throw new Exception($"Invalid row type for table {RemoteTableName}: {newValue.GetType().Name}");
                }
            }
        }

        /// <summary>
        /// Invoked after applying the parsed table update (delta) to this table.
        /// This is when user callbacks (such as OnInsert, OnUpdate, and OnDelete) are actually triggered for the affected rows.
        /// All <see cref="IRemoteTableHandle.Apply"/> operations should be complete before calling PostApply,
        /// so that data structures across all tables are fully updated before invoking user callbacks.
        /// Should be called after PreApply and Apply.
        /// </summary>
        void IRemoteTableHandle.PostApply(IEventContext context)
        {
            foreach (var (_, value) in wasInserted)
            {
                InvokeInsert(context, value);
            }
            if (!IsEventTable)

View on GitHub (pinned to 9e0d92412f)

Solutions

  1. Regenerate bindings so updated values are instances of the generated Row subclass.
  2. Remove hand edits to generated files and derive any custom row types from SpacetimeDB.Runtime.Row.
  3. If reproducible with clean generated code, file an SDK issue including the table name and update delta.
Defensive patterns

Strategy: type-guard

Type guard

static bool IsRowType(Type t) => typeof(SpacetimeDB.Runtime.Row).IsAssignableFrom(t);

Try / catch

conn.SubscriptionBuilder().OnError(ex => Log.Error($"update row invariant broken: {ex.Message}"));

Prevention

When it happens

Trigger: An update delta materializes into a non-Row object: generated row type not deriving from Row (stale or hand-edited generated code), or a custom value factory used by the table implementation.

Common situations: Hand-edited generated bindings; SDK/package version skew between the runtime assembly and the generated table classes.

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/1b2a697bb5c027a3. Report an issue: GitHub.