clockworklabs/SpacetimeDB · error · InvalidOperationException

Unsupported system type

Error message

Unsupported system type {name}

What it means

For named System.* types, GetTypeInfoForNamedType only knows System.Int128/UInt128, SpacetimeDB.I128/U128/I256/U256, List<T>, IEnumerable<T> and Nullable<T>. Any other type whose original-definition name starts with 'System.' — Dictionary<K,V>, HashSet<T>, Guid, DateTime, TimeSpan, KeyValuePair, System.Numerics.* — throws InvalidOperationException, refusing to guess a BSATN layout for BCL types it has no mapping for.

Solutions

  1. Replace Dictionary<K,V> with List<T> of a [SpacetimeDB.Type] key/value entry struct
  2. Use SpacetimeDB.Identity instead of Guid for player/entity ids
  3. Map DateTime/DateTimeOffset/TimeSpan to long (ticks or unix microseconds)
  4. Replace HashSet<T> with List<T> and dedupe inside reducer logic

Example fix

// before
[SpacetimeDB.Type]
public partial struct Settings {
    public Dictionary<string, int> Flags;
    public Guid PlayerGuid;
    public DateTime UpdatedAt;
}

// after
[SpacetimeDB.Type]
public partial struct FlagEntry { public string Key; public int Value; }

[SpacetimeDB.Type]
public partial struct Settings {
    public List<FlagEntry> Flags;
    public SpacetimeDB.Identity PlayerId;
    public long UpdatedAtMicros;
}
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time: the generator rejects unmapped System.* types.
// Replacement map to apply to schema members:
//   Dictionary<K,V> -> List<EntryStruct> ([SpacetimeDB.Type] { K Key; V Value; })
//   HashSet<T>      -> List<T>
//   Guid            -> SpacetimeDB.Identity (or byte[16])
//   DateTime        -> long micros
//   TimeSpan        -> long micros

Try / catch

// compile-time error: nothing to catch. Replace the System.* type and rebuild.

Prevention

When it happens

Trigger: Using Dictionary<K,V>, HashSet<T>, KeyValuePair<,>, Guid, DateTime, DateTimeOffset, TimeSpan, System.Numerics.BigInteger or any unmapped System.* type in a table column, reducer argument, or [SpacetimeDB.Type] member.

Common situations: Bringing in domain models with dictionary lookups; using Guid as a player identifier where SpacetimeDB.Identity is intended; DateTime timestamps; HashSet sets copied from server logic.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/871ee7ba506f358e. Report an issue: GitHub.

Appendix: source

Thrown at crates/bindings-csharp/BSATN.Codegen/Utils.cs:181

                        $"Enum {type} does not have a [SpacetimeDB.Type] attribute"
                    );
                }
                return $"SpacetimeDB.BSATN.Enum<{SymbolToName(type)}>";
            }
            var result = type.OriginalDefinition.ToString() switch
            {
                // {U/I}{128/256} are not treated by C# as regular primitives, so we need to match them by type name.
                "System.Int128" => "SpacetimeDB.BSATN.I128",
                "System.UInt128" => "SpacetimeDB.BSATN.U128",
                "SpacetimeDB.I128" => "SpacetimeDB.BSATN.I128Stdb",
                "SpacetimeDB.U128" => "SpacetimeDB.BSATN.U128Stdb",
                "SpacetimeDB.I256" => "SpacetimeDB.BSATN.I256",
                "SpacetimeDB.U256" => "SpacetimeDB.BSATN.U256",
                "System.Collections.Generic.List<T>" => $"SpacetimeDB.BSATN.List",
                "System.Collections.Generic.IEnumerable<T>" => $"SpacetimeDB.BSATN.List",
                // If we're here, then this is nullable *value* type like `int?`.
                "System.Nullable<T>" => $"SpacetimeDB.BSATN.ValueOption",
                var name when name.StartsWith("System.") => throw new InvalidOperationException(
                    $"Unsupported system type {name}"
                ),
                _ => $"{SymbolToName(type)}.BSATN",
            };
            if (type.IsGenericType)
            {
                result =
                    $"{result}<{string.Join(", ", type.TypeArguments.Select(SymbolToName).Concat(type.TypeArguments.Select(GetTypeInfo)))}>";
            }

            return result;
        }
    }

    // Polyfill for .NET methods from .NET Standard 2.1+:
    private static StringBuilder AppendJoin<T>(
        this StringBuilder sb,
        string separator,

View on GitHub (pinned to 6dee26c6ef)