Skip to content

# TypeScript codegen: PascalCase table keys in tablesSchema cause 'columns' undefined crash on subscription #4433

@Reperion

Description

@Reperion

Love SpaceTimeDB, congrats on your launch of 2.0, and that video was EPIC :D

Since I am a n00b coder, I ran into an issue and had an a.i. summarise;

Bug Summary

The TypeScript code generator (spacetime generate --lang typescript) produces PascalCase keys in the client tablesSchema object (e.g., Report, ReportCategory), but the SpacetimeDB wire protocol sends table names using the actual snake_case name field values (e.g., report, report_category). The SDK looks up tables by sourceName which is set to the PascalCase key, causing a crash when processing subscription messages.

Error

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'columns')
    at DbConnection.parseRowList_fn (spacetimedb.js:6484:46)
    at DbConnection.queryRowsToTableUpdates_fn (spacetimedb.js:6532:87)
    at DbConnection.processMessage_fn (spacetimedb.js:6653:107)

Version

  • SpacetimeDB CLI: 2.0.1 (commit a4d29daec8ed35ce4913a335b7210b9ae3933d00)
  • spacetimedb npm package: 2.0.1
  • Module language: TypeScript
  • Client framework: React (via spacetimedb/react)

Root Cause

In the SDK source at src/lib/schema.ts:94, tableToSchema() sets sourceName: accName where accName is the object key from the generated tablesSchema:

// schema.ts line 93-95
return {
  sourceName: accName,   // ← set to the PascalCase key
  accessorName: accName,
  ...
};

In src/sdk/db_connection_impl.ts:207-216, the #sourceNameToTableDef map is keyed by table.sourceName:

this.#sourceNameToTableDef[table.sourceName] = table;

When processing subscription messages at line 427, the SDK looks up the table using the wire table name (snake_case from the server):

const table = this.#sourceNameToTableDef[tableName];  // tableName = "report" from wire
const columnsArray = Object.entries(table.columns);    // table is undefined → crash

The mismatch: the map has key "Report" (PascalCase from codegen) but the wire sends "report" (snake_case from DB).

Reproduction

  1. Create a TypeScript module with a table:
import { schema, table, t } from 'spacetimedb/server';

const User = table(
  { name: 'user', public: true },
  { identity: t.identity().primaryKey(), username: t.string().optional() }
);

const spacetimedb = schema({ User });
export default spacetimedb;
  1. Publish and generate bindings:
spacetime publish mydb --no-config -s local
spacetime generate --lang typescript --out-dir ./src/module_bindings --module-path ./spacetimedb
  1. The generated src/module_bindings/index.ts contains:
const tablesSchema = __schema({
  User: __table({ name: 'user', ... }, UserRow),  // ← PascalCase key "User"
});
  1. Connect from a client and subscribe:
conn.subscriptionBuilder().subscribe(['SELECT * FROM user']);
  1. Crash — the server sends table name "user" but the SDK only has "User" in its lookup map.

Workaround

Manually edit the generated index.ts to use snake_case keys matching the name field:

const tablesSchema = __schema({
  user: __table({ name: 'user', ... }, UserRow),           // was: User
  report_category: __table({ name: 'report_category', ... }, ReportCategoryRow),  // was: ReportCategory
});

Then update all client references from tables.User to tables.user, etc.

Suggested Fix

The codegen should use the table's name field value as the key in tablesSchema, not the PascalCase-converted name. Alternatively, tableToSchema() could read the name from the table definition and use that as sourceName instead of accName.

Environment

  • macOS 25.3.0 (Apple Silicon)
  • Node.js v25.4.0
  • Bun v1.3.8

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions