-
Notifications
You must be signed in to change notification settings - Fork 732
Open
Description
Description
When a TypeScript module defines a table with two or more explicit btree indexes and the user does not provide the optional accessor field, all reducers PANIC at runtime with:
TypeError: Cannot assign to read only property 'filter' of object '#<Object>'
in makeTableView
A single explicit index works fine. The crash only occurs with 2+ explicit indexes on the same table.
Reproduction
Minimal module that crashes:
import { schema, table, t } from 'spacetimedb/server';
const task = table(
{
public: true,
indexes: [
{ name: 'task_assigned_to', algorithm: 'btree' as const, columns: ['assignedTo'] },
{ name: 'task_status', algorithm: 'btree' as const, columns: ['status'] },
],
},
{
id: t.u64().primaryKey().autoInc(),
title: t.string(),
status: t.string(),
assignedTo: t.string(),
}
);
const spacetimedb = schema({ task });
export default spacetimedb;
export const addTask = spacetimedb.reducer(
{ title: t.string() },
(ctx, { title }) => {
ctx.db.task.insert({ id: 0n, title, status: 'pending', assignedTo: 'nobody' });
}
);spacetime publish my-test --module-path . -s local -y
spacetime call my-test add_task '"hello"' -s local
# → PANIC: Cannot assign to read only property 'filter'Remove either index and the reducer works.
Root Cause
In crates/bindings-typescript/src/lib/table.ts line 428-433, explicit indexes are pushed with:
indexes.push({
sourceName: undefined,
accessorName: indexOpts.accessor, // undefined when user omits `accessor`
algorithm,
canonicalName: indexOpts.name,
});Since accessor is optional in IndexOpts, omitting it sets accessorName: undefined for every explicit index.
Then in crates/bindings-typescript/src/server/runtime.ts lines 798-802:
if (Object.hasOwn(tableView, indexDef.accessorName!)) {
freeze(Object.assign(tableView[indexDef.accessorName!], index));
} else {
tableView[indexDef.accessorName!] = freeze(index) as any;
}- First index:
Object.hasOwn(tableView, undefined)→ false →tableView["undefined"] = freeze(index)— the index object (which has afilterproperty) is frozen - Second index:
Object.hasOwn(tableView, undefined)→ true →Object.assign(tableView["undefined"], index)— tries to writefilteronto the frozen object → crash
Workaround
Add accessor to every explicit index definition:
indexes: [
{ accessor: 'task_assigned_to', name: 'task_assigned_to', algorithm: 'btree', columns: ['assignedTo'] },
{ accessor: 'task_status', name: 'task_status', algorithm: 'btree', columns: ['status'] },
]Suggested Fix
Either:
- Derive
accessorNamefromcanonicalName(thenamefield) whenaccessoris not provided, so each index gets a unique accessor - Guard
runtime.tsagainstundefinedaccessor names (skip or throw a clear error)
Environment
- SpacetimeDB CLI: 2.0.1
- npm
spacetimedb: 2.0.1 - macOS 15.5 (Apple Silicon)
- Node.js v24.13
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels