clockworklabs/SpacetimeDB · error · TypeError
Index '${indexLabel}' on table '${tableLabel}' must define a
Error message
Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor' What it means
When table.ts converts the user-declared options.indexes entries into raw index metadata, each entry needs a non-empty accessor string - it becomes the property name under the table handle used for index-scoped queries. An entry with a missing or empty accessor throws a TypeError naming the table and index.
Source
Thrown at crates/bindings-typescript/src/lib/table.ts:442
colId: colIds.get(name)!,
value: writer.getBuffer(),
});
}
// If this column is shaped like ScheduleAtAlgebraicType, mark it as the schedule‑at column
const algebraicType = builder.typeBuilder.algebraicType;
if (ScheduleAt.isScheduleAt(algebraicType)) {
scheduleAtCol = colIds.get(name)!;
}
}
// convert explicit multi‑column indexes coming from options.indexes
for (const indexOpts of userIndexes ?? []) {
const accessor = indexOpts.accessor;
if (typeof accessor !== 'string' || accessor.length === 0) {
const tableLabel = name ?? '<unnamed>';
const indexLabel = indexOpts.name ?? '<unnamed>';
throw new TypeError(
`Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'`
);
}
let algorithm: RawIndexAlgorithm;
switch (indexOpts.algorithm) {
case 'btree':
algorithm = {
tag: 'BTree',
value: indexOpts.columns.map(c => colIds.get(c)!),
};
break;
case 'hash':
algorithm = {
tag: 'Hash',
value: indexOpts.columns.map(c => colIds.get(c)!),
};
break;
case 'direct':View on GitHub (pinned to 524b4487d9)
Solutions
- Add a non-empty accessor to every entry: { name: 'by_email', accessor: 'byEmail', algorithm: 'btree', columns: ['email'] }
- Adopt a convention (e.g. accessor equals the camelCased name) so entries are never left without one
- Type the options object against the library's exported index-options interface so omissions surface at compile time
Example fix
// before
indexes: [{ name: 'by_email', algorithm: 'btree', columns: ['email'] }]
// after
indexes: [{ name: 'by_email', accessor: 'byEmail', algorithm: 'btree', columns: ['email'] }] Defensive patterns
Strategy: validation
Validate before calling
function validateIndexOptions(indexes: Array<{ name?: string; accessor?: unknown }>): void {
for (const idx of indexes) {
if (typeof idx.accessor !== 'string' || idx.accessor.length === 0) {
throw new TypeError(`index '${idx.name ?? '<unnamed>'}' must define a non-empty 'accessor'`);
}
}
} Prevention
- Always set accessor alongside name in every indexes entry
- Type table options against the library's exported interfaces so omissions fail at compile time
- Add a startup validation pass over your table declarations to catch empty accessors early
When it happens
Trigger: Declaring a table with indexes: [{ name: 'by_email', algorithm: 'btree', columns: ['email'] }] - no accessor field - or accessor: ''.
Common situations: Copying index examples from older docs where accessor was optional or inferred; renaming accessors and leaving one empty; hand-writing the options object without the compiler checking it.
Related errors
- Unknown index algorithm {:?}
- Index '${idx.sourceName ?? '<unknown>'}' on table '${tableDe
- Unknown constraint type
- unknown misc export
- source_name should be provided in V10, accessor_names inside
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/88fb7542690aff9e.
Report an issue: GitHub.