-
Notifications
You must be signed in to change notification settings - Fork 684
Description
Bug Description
In the TypeScript SDK v1.12.0, ArrayBuilder.name() and ArrayBuilder.default() pass this.element instead of this to ArrayColumnBuilder, which
strips the Array<> type wrapper. This causes any table with Vec<> fields to have an incorrect schema, leading to BSATN deserialization failures at
runtime.
Affected Code
File: src/lib/type_builders.ts (and all compiled dist files)
// Current (broken):
export class ArrayBuilder<Element extends TypeBuilder<any, any>> extends TypeBuilder<...> {
default(value) {
return new ArrayColumnBuilder(
this.element, // ❌ passes the inner element, stripping Array<> wrapper
set(defaultMetadata, { defaultValue: value })
);
}
name(name) {
return new ArrayColumnBuilder(this.element, set(defaultMetadata, { name })); // ❌ same bug
}
}
// Fix:
default(value) {
return new ArrayColumnBuilder(
this, // ✅ preserves Array<> type
set(defaultMetadata, { defaultValue: value })
);
}
name(name) {
return new ArrayColumnBuilder(this, set(defaultMetadata, { name })); // ✅
}Note: OptionBuilder has the correct implementation (this instead of this.value), so this bug is specific to ArrayBuilder.
How It Triggers
The auto-generated TypeScript bindings call .name() on any field whose camelCase JS name differs from the snake_case SpacetimeDB column name. For
example:
// Generated by: spacetime generate --lang typescript
export default __t.row({
playerUserIds: __t.array(__t.u64()).name("player_user_ids"), // triggers bug
playerUsernames: __t.array(__t.string()).name("player_usernames"), // triggers bug
});Because .name() passes this.element instead of this, the table schema registers these fields as u64 and string instead of Array<u64> and
Array<string>.
Runtime Error
When the SDK receives subscription data containing rows for these tables, the BSATN deserializer reads bytes using the wrong type schema. A u64 reads 8
bytes where Vec<u64> should read a 4-byte length prefix + N*8 bytes of data. This misaligns the entire byte stream, causing subsequent Option<> fields
to read garbage tag values:
Can't deserialize an option type, couldn't find 108 tag
(Expected tag 0=Some or 1=None, got 108 due to byte stream misalignment)
Reproduction
- Create a SpacetimeDB module with a table containing a
Vec<>field with a snake_case name - Generate TypeScript bindings (
spacetime generate --lang typescript) - Subscribe to that table when it contains at least one row
- Observe BSATN deserialization error
Workaround
Apply this sed command after npm install:
sed -i 's/return new ArrayColumnBuilder(this\.element,/return new ArrayColumnBuilder(this,/g' \
node_modules/spacetimedb/dist/index.browser.mjs \
node_modules/spacetimedb/dist/index.mjs \
node_modules/spacetimedb/dist/index.cjs \
node_modules/spacetimedb/src/lib/type_builders.tsEnvironment
spacetimedbnpm package: 1.12.0spacetimeCLI: 1.12.0- SpacetimeDB server: v1.12.0