clockworklabs/SpacetimeDB · error · Error
cannot serialize refs without a typespace
Error message
cannot serialize refs without a typespace
What it means
AlgebraicType.makeSerializer(ty, typespace) builds a serializer for a SATS algebraic type. A Ref type is only an index into a typespace (the array of types emitted with a module's schema), so when ty.tag === 'Ref' and no typespace was passed there is no way to resolve the referenced type. The library throws instead of guessing and emitting corrupt bytes.
Source
Thrown at crates/bindings-typescript/src/lib/algebraic_type.ts:131
I16: { tag: 'I16' } as const,
U16: { tag: 'U16' } as const,
I32: { tag: 'I32' } as const,
U32: { tag: 'U32' } as const,
I64: { tag: 'I64' } as const,
U64: { tag: 'U64' } as const,
I128: { tag: 'I128' } as const,
U128: { tag: 'U128' } as const,
I256: { tag: 'I256' } as const,
U256: { tag: 'U256' } as const,
F32: { tag: 'F32' } as const,
F64: { tag: 'F64' } as const,
makeSerializer(
ty: AlgebraicTypeType,
typespace?: TypespaceType
): Serializer<any> {
if (ty.tag === 'Ref') {
if (!typespace)
throw new Error('cannot serialize refs without a typespace');
while (ty.tag === 'Ref') ty = typespace.types[ty.value];
}
switch (ty.tag) {
case 'Product':
return ProductType.makeSerializer(ty.value, typespace);
case 'Sum':
return SumType.makeSerializer(ty.value, typespace);
case 'Array':
if (ty.value.tag === 'U8') {
return serializeUint8Array;
} else {
const serialize = AlgebraicType.makeSerializer(ty.value, typespace);
return (writer, value) => {
writer.writeU32(value.length);
for (const elem of value) {
serialize(writer, elem);
}
};View on GitHub (pinned to 524b4487d9)
Solutions
- Pass the module's typespace as the second argument: AlgebraicType.makeSerializer(ty, typespace)
- Or dereference the ref yourself first: while (ty.tag === 'Ref') ty = typespace.types[ty.value]
- Prefer the schema-generated table/reducer bindings, which always carry the typespace
Example fix
// before
const ser = AlgebraicType.makeSerializer(ty); // ty = { tag: 'Ref', value: 3 } -> throws
// after
const ser = AlgebraicType.makeSerializer(ty, schema.typespace); Defensive patterns
Strategy: validation
Validate before calling
function assertSerializableType(ty: AlgebraicTypeType, typespace?: TypespaceType): void {
if (ty.tag === 'Ref' && !typespace) {
throw new Error(`Ref type (index ${ty.value}) cannot be serialized without a typespace`);
}
}
// assertSerializableType(ty, typespace) before makeSerializer Type guard
const isRefType = (ty: AlgebraicTypeType): ty is { tag: 'Ref'; value: number } =>
ty.tag === 'Ref'; Prevention
- Always thread the typespace through recursive serializer-building code
- Prefer schema-generated bindings over hand-built serializers
- Treat a Ref type without a typespace as a programming bug: fail fast in dev with a clear message
When it happens
Trigger: Calling AlgebraicType.makeSerializer({ tag: 'Ref', value: n }) or AlgebraicType.serializeValue(writer, refTy, value) without the second typespace argument; hand-building serializers from module schema metadata and dropping the typespace when recursing into Product/Sum elements.
Common situations: Writing custom row encoders over raw AlgebraicType metadata; porting code from an older bindings version where the typespace argument was not threaded through recursive calls; passing only the inner type of a Ref-containing product while forgetting the containing schema's typespace.
Related errors
- cannot deserialize refs without a typespace
- could not serialize result: object had neither a `ok` nor an
- Could not serialize sum type; unknown tag ${value.tag}
- Missing type name for ${typeBuilder.constructor.name ?? 'Typ
- Cannot extract table name from query
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/afeea8b50b21247b.
Report an issue: GitHub.