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

  1. 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).
  2. Ensure recursive types are declared via the typespace's self-reference mechanism (add placeholder then fill) instead of expanding forever.
  3. Reuse a single Typespace per module/conversion rather than building a new one per item in a loop.
  4. 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

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


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