clockworklabs/SpacetimeDB · error · Error
cannot deserialize refs without a typespace
Error message
cannot deserialize refs without a typespace
What it means
AlgebraicType.makeDeserializer(ty, typespace) mirrors makeSerializer for decoding. A Ref algebraic type is just an index into a typespace; without that array the concrete type cannot be resolved, so the method throws immediately rather than producing a decoder that would misread the stream.
Source
Thrown at crates/bindings-typescript/src/lib/algebraic_type.ts:170
return primitiveSerializers[ty.tag];
}
},
/** @deprecated Use `makeSerializer` instead. */
serializeValue(
writer: BinaryWriter,
ty: AlgebraicTypeType,
value: any,
typespace?: TypespaceType
) {
AlgebraicType.makeSerializer(ty, typespace)(writer, value);
},
makeDeserializer(
ty: AlgebraicTypeType,
typespace?: TypespaceType
): Deserializer<any> {
if (ty.tag === 'Ref') {
if (!typespace)
throw new Error('cannot deserialize refs without a typespace');
while (ty.tag === 'Ref') ty = typespace.types[ty.value];
}
switch (ty.tag) {
case 'Product':
return ProductType.makeDeserializer(ty.value, typespace);
case 'Sum':
return SumType.makeDeserializer(ty.value, typespace);
case 'Array':
if (ty.value.tag === 'U8') {
return deserializeUint8Array;
} else {
const deserialize = AlgebraicType.makeDeserializer(
ty.value,
typespace
);
return reader => {
const length = reader.readU32();
const result: any[] = Array(length);View on GitHub (pinned to 524b4487d9)
Solutions
- Pass the typespace as the second argument: AlgebraicType.makeDeserializer(ty, typespace)
- Or resolve refs up front: while (ty.tag === 'Ref') ty = typespace.types[ty.value]
- Use generated bindings which pass the typespace automatically
Example fix
// before const deser = AlgebraicType.makeDeserializer(refTy); // -> throws // after const deser = AlgebraicType.makeDeserializer(refTy, schema.typespace);
Defensive patterns
Strategy: validation
Validate before calling
function assertDeserializableType(ty: AlgebraicTypeType, typespace?: TypespaceType): void {
if (ty.tag === 'Ref' && !typespace) {
throw new Error(`Ref type (index ${ty.value}) cannot be deserialized without a typespace`);
}
} Type guard
const isRefType = (ty: AlgebraicTypeType): ty is { tag: 'Ref'; value: number } =>
ty.tag === 'Ref'; Prevention
- Keep the typespace next to any AlgebraicType you store for decoding
- Write one helper that resolves refs (while (ty.tag === 'Ref')) and use it everywhere
- Add a unit test that round-trips every schema type to catch missing-typespace paths
When it happens
Trigger: Calling AlgebraicType.makeDeserializer({ tag: 'Ref', value: n }) without the typespace argument; building deserializers by hand from raw schema metadata (e.g. custom clients decoding column values) and not threading the typespace through recursive calls.
Common situations: Custom decode paths over module schema metadata; code copied from samples that predate the typespace parameter; refactoring that passes ty.value of a variant element but loses the outer typespace.
Related errors
- cannot serialize refs without a typespace
- Missing type name for ${typeBuilder.constructor.name ?? 'Typ
- could not serialize result: object had neither a `ok` nor an
- Could not serialize sum type; unknown tag ${value.tag}
- Tried to read ${n} byte(s) at relative offset ${this.offset}
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/c90c84457a8dead7.
Report an issue: GitHub.