clockworklabs/SpacetimeDB · error
ran out of space for `AlgebraicTypeRef`s
Error message
ran out of space for `AlgebraicTypeRef`s
What it means
Panic in `Typespace::add`: SpacetimeDB's typespace indexes types with `AlgebraicTypeRef(u32)`; adding a type when the typespace already holds `u32::MAX` entries fails `try_into().expect("ran out of space for AlgebraicTypeRefs")`. Reaching ~4.29 billion type definitions is essentially impossible for real modules, so this panic almost always indicates runaway recursion in type generation, not a legitimately large schema.
Source
Thrown at crates/sats/src/typespace.rs:106
self.types.get_mut(r.idx())
}
/// Inserts an `AlgebraicType` into the typespace
/// and returns an `AlgebraicTypeRef` that refers to the inserted `AlgebraicType`.
///
/// This allows for self referential,
/// recursive or other complex types to be declared in the typespace.
///
/// You can also use this to later change the meaning of the returned `AlgebraicTypeRef`
/// when you cannot provide the full definition of the type yet.
///
/// Panics if the number of type references exceeds an `u32`.
pub fn add(&mut self, ty: AlgebraicType) -> AlgebraicTypeRef {
let index = self
.types
.len()
.try_into()
.expect("ran out of space for `AlgebraicTypeRef`s");
self.types.push(ty);
AlgebraicTypeRef(index)
}
/// Returns `ty` combined with the context `self`.
pub const fn with_type<'a, T: ?Sized>(&'a self, ty: &'a T) -> WithTypespace<'a, T> {
WithTypespace::new(self, ty)
}
/// Returns the `AlgebraicType` that `r` resolves to in the context of the `Typespace`.
///
/// Panics if `r` is not known by the `Typespace`.
///
/// Note, this is not recursive.
/// To resolve all nested refs, call `resolve_refs()` on the result.
pub fn resolve(&self, r: AlgebraicTypeRef) -> WithTypespace<'_, AlgebraicType> {
self.with_type(&self[r])View on GitHub (pinned to 6dee26c6ef)
Solutions
- Profile/inspect where `add` is called in a loop — look for recursion where a type's expansion produces new types indefinitely; add a visited/memo set (use the typespace's own dedup, e.g. an `add_type`-style API that intern's existing types).
- Ensure recursive types are declared via the typespace's self-reference mechanism (add placeholder then fill) instead of expanding forever.
- Reuse a single Typespace per module/conversion rather than building a new one per item in a loop.
- If it genuinely fired from schema size (astronomically unlikely), split the module — but treat it as a recursion bug first.
Example fix
// before: unbounded recursion adding new types forever
fn add_all(ts: &mut Typespace, ty: &AlgebraicType) {
for elem in ty.children() { ts.add(elem.clone()); add_all(ts, &elem); } // no visited set
}
// after: memoize via interning so re-encountered types aren't re-added
fn add_all(ts: &mut Typespace, ty: &AlgebraicType, seen: &mut HashSet<AlgebraicTypeRef>) {
let r = ts.add_type(ty); // dedup interning
if !seen.insert(r) { return; }
for elem in ty.children() { add_all(ts, &elem, seen); }
} Defensive patterns
Strategy: validation
Validate before calling
// Cap recursion depth in your own type-walking code
fn walk(ts: &mut Typespace, ty: &AlgebraicType, depth: usize) {
if depth > 1024 { panic_or_err!("recursive type expansion too deep"); }
/* intern via ts.add_type (dedup) rather than ts.add when re-visiting */
} Prevention
- Intern types via the typespace's deduplicating API instead of raw `add` in loops.
- Maintain a visited set when expanding recursive/self-referential schemas.
- Reuse one Typespace per module; never build a fresh one per item in a hot loop.
When it happens
Trigger: Repeatedly calling `typespace.add(ty)` in a loop that never terminates — typically a recursive schema where adding type A triggers synthesis of more types forever (missing memoization/visited-set), or a codegen/deserializer loop feeding its own output back into `add`. The panic is the safety net that stops the infinite loop at 4 billion entries (long after memory pressure would usually kill the process).
Common situations: Self-referential type generation without cycle detection; a bug where the same types are re-added instead of deduplicated via `add_type`; fuzzers generating unbounded recursive schemas; building typespaces in a hot loop without reuse.
Related errors
- length didn't fit in `u32`
- unable to decode args
- unable to encode view return value
- Failed to generate table due to validation errors
- table schema should validate for query builder codegen
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/a0c4bb831cd89463.
Report an issue: GitHub.