clockworklabs/SpacetimeDB · error · Exception
Invalid row type for table {RemoteTableName}: {oldValue.GetT
Error message
Invalid row type for table {RemoteTableName}: {oldValue.GetType().Name} What it means
In the wasUpdated loop of Table.Apply, the OLD value for each updated key must be a Row so the internal delete handler can fire for it. This variant means the previously cached value (not the incoming one) is not a Row instance, i.e. the cache was populated with non-Row values at some earlier point. It indicates the same class of invariant break as the insert variant, but surfaces on the update path.
Source
Thrown at sdks/csharp/src/Table.cs:531
{
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);
}
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,View on GitHub (pinned to 9e0d92412f)
Solutions
- Regenerate all bindings with the CLI version matching the SDK package so every table's TValue is Row-derived.
- Audit custom table/IRemoteTableHandle implementations and derive row types from Row.
- If fully generated, report with the table name and delta - old cache values of non-Row type should be impossible.
Defensive patterns
Strategy: type-guard
Type guard
static bool IsRowType(Type t) => typeof(SpacetimeDB.Runtime.Row).IsAssignableFrom(t); // assert for all cached row types
Try / catch
conn.SubscriptionBuilder().OnError(ex => Log.Error($"cached row invariant broken: {ex.Message}")); Prevention
- Regenerate all bindings together with SDK upgrades - partial regeneration leaves non-Row cached values.
- Do not persist or inject custom values into table caches.
When it happens
Trigger: A row update arrives for a table whose cache was earlier populated with non-Row values: custom table handle with a plain class as TValue, or mixed-version generated code where insert-variant checks were skipped (older SDK) and an update now hits the check.
Common situations: Partial upgrade: client assembly updated but some generated table files regenerated with an older toolchain; custom table implementations in test harnesses or fixtures.
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}: {value.GetType
- 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/24d75550cf5587fc.
Report an issue: GitHub.