clockworklabs/SpacetimeDB · error
recursive types not supported
Error message
recursive types not supported
What it means
`resolved_type_via_v9<T>()` builds a raw module-def for T and resolves all type references; `resolve_refs` fails (triggering this expect) when the typespace still contains unresolvable refs, which is exactly what recursive types produce — a ref cycle can never be inlined into a single AlgebraicType. SpacetimeDB's type system does not support recursive schema types, so this is a hard error by design.
Source
Thrown at crates/lib/src/lib.rs:341
} else {
hex
};
hex::FromHex::from_hex(hex)
}
/// Returns a resolved `AlgebraicType` (containing no `AlgebraicTypeRefs`) for a given `SpacetimeType`,
/// using the v9 moduledef infrastructure.
/// Panics if the type is recursive.
///
/// TODO: we could implement something like this in `sats` itself, but would need a lightweight `TypespaceBuilder` implementation there.
pub fn resolved_type_via_v9<T: SpacetimeType>() -> AlgebraicType {
let mut builder = RawModuleDefV9Builder::new();
let ty = T::make_type(&mut builder);
let module = builder.finish();
WithTypespace::new(&module.typespace, &ty)
.resolve_refs()
.expect("recursive types not supported")
}
View on GitHub (pinned to 524b4487d9)
Solutions
- Break the cycle: model self-reference through table rows and row ids instead of nested types.
- Flatten the structure: store nodes in a table (id, value, parent_id) or use an explicit arena (Vec<Node> + usize links) for internal logic.
- Keep recursive types out of SpacetimeType definitions entirely; use them only in module-internal, non-schema code.
Example fix
// before: recursive type — resolve_refs can never finish
#[derive(spacetimedb::SpacetimeType)]
struct Node { value: u64, next: Option<Box<Node>> }
// after: normalize via a table keyed by id
#[derive(spacetimedb::SpacetimeType)]
struct Node { id: u64, value: u64, parent: Option<u64> } // rows in table `node` Defensive patterns
Strategy: validation
Validate before calling
// CI test: any type passed to resolved_type_via_v9 must resolve
#[test]
fn schema_resolves() { let _ = resolved_type_via_v9::<MyType>(); } Type guard
trait NonRecursiveType: spacetimedb::SpacetimeType {} // marker implemented only for types covered by the resolving test Prevention
- Never nest a type inside itself (even via Box/Option) in SpacetimeType definitions.
- Model graphs with tables plus row ids instead of nested recursive structures.
- Run a schema-construction test for every public module type in CI.
When it happens
Trigger: Calling `resolved_type_via_v9` (or APIs that derive schema from a type) with T that transitively contains itself: `struct Node { next: Option<Box<Node>> }`, mutually recursive structs/enums, or any type whose expansion does not terminate.
Common situations: Porting linked-list/tree types from general Rust into SpacetimeDB modules; reusing recursive serde types as table rows, reducer args, or return values; tooling that inspects arbitrary user-supplied types.
Related errors
- never types are not yet supported in C# output
- expected ModuleDef to contain key, but it does not
- source_name should be provided in V10, accessor_names inside
- cannot serialize refs without a typespace
- cannot deserialize refs without a typespace
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/b4b5dae408ebd752.
Report an issue: GitHub.