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
- Give the type a name: t.object('Player', {...}) or t.enum('Status', {...})
- Keep anonymous builders for nested positions only; anything registered at top level must be named
- 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
- Name every top-level type builder: t.object('Player', {...}), t.enum('Status', {...})
- Reserve anonymous builders for nested positions only
- Lint for t.object(/t.enum( calls whose first argument is not a string at top level
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
- cannot serialize refs without a typespace
- cannot deserialize refs without a typespace
- When providing a name, you must also provide the variants ob
- When providing a name, you must also provide the object.
- never types are not yet supported in C# output
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/0600a8f1cb852858.
Report an issue: GitHub.