clockworklabs/SpacetimeDB · error · Error

Missing type name for ${typeBuilder.constructor.name ?? 'Typ

Error message

Missing type name for ${typeBuilder.constructor.name ?? 'TypeBuilder'} ${JSON.stringify(typeBuilder)}

What it means

SchemaBuilder's #registerCompoundTypeRecursively inserts compound types into the typespace so nested and recursive types can be referenced by Ref. Its documented contract (see the NB comment) is that callers only pass builders that already carry a typeName; a top-level anonymous builder would generate types the codegen cannot name or reference, so it fails fast with the builder's constructor name and a JSON dump.

Source

Thrown at crates/bindings-typescript/src/lib/schema.ts:361

      ) as any;
    } else {
      return typeBuilder as any;
    }
  }

  #registerCompoundTypeRecursively<
    T extends
      | SumBuilder<VariantsObj>
      | ProductBuilder<ElementsObj>
      | RowBuilder<RowObj>,
  >(typeBuilder: T): RefBuilder<Infer<T>, InferSpacetimeTypeOfTypeBuilder<T>> {
    const ty = typeBuilder.algebraicType;
    // NB! You must ensure that all TypeBuilder passed into this function
    // have a name. This function ensures that nested types always have a
    // name by assigning them one if they are missing it.
    const name = typeBuilder.typeName;
    if (name === undefined) {
      throw new Error(
        `Missing type name for ${typeBuilder.constructor.name ?? 'TypeBuilder'} ${JSON.stringify(typeBuilder)}`
      );
    }

    let r = this.#compoundTypes.get(ty);
    if (r != null) {
      // Already added to typespace
      return r;
    }

    // Recursively register nested compound types
    const newTy =
      typeBuilder instanceof RowBuilder || typeBuilder instanceof ProductBuilder
        ? ({
            tag: 'Product',
            value: { elements: [] },
          } as AlgebraicTypeVariants.Product)
        : ({

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Give the type a name: t.object('Player', {...}) or t.enum('Status', {...})
  2. Keep anonymous builders for nested positions only; anything registered at top level must be named
  3. Use the error's constructor.name and JSON dump to locate which builder was anonymous

Example fix

// before
const player = t.object({ name: t.string, hp: t.u32 }); // anonymous at registration -> throws

// after
const player = t.object('Player', { name: t.string, hp: t.u32 });
Defensive patterns

Strategy: validation

Validate before calling

function assertNamedBuilder(b: { typeName?: unknown; constructor: { name: string } }): void {
  if (b.typeName === undefined) {
    throw new Error(`refusing to register anonymous ${b.constructor.name}: give it a name`);
  }
}

Type guard

function isNamedBuilder<T extends { typeName?: string }>(b: T): b is T & { typeName: string } {
  return b.typeName !== undefined;
}

Prevention

When it happens

Trigger: Registering an anonymous builder - t.object({...}) or t.enum({...}) with no first name argument - through a schema-builder path that does not auto-assign nested names; calling the schema registration internals directly with inline builders.

Common situations: Refactoring type-builder declarations and dropping the name argument; moving a nested anonymous type into a top-level registration slot; copy-pasting a builder literal that never had a name.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/0600a8f1cb852858. Report an issue: GitHub.