{"record":{"id":"88fb7542690aff9e","repo":"clockworklabs/SpacetimeDB","slug":"index-indexlabel-on-table-tablelabel-mus","errorCode":null,"errorMessage":"Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'","messagePattern":"Index '(.+?)' on table '(.+?)' must define a non-empty 'accessor'","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/lib/table.ts","lineNumber":442,"sourceCode":"        colId: colIds.get(name)!,\n        value: writer.getBuffer(),\n      });\n    }\n\n    // If this column is shaped like ScheduleAtAlgebraicType, mark it as the schedule‑at column\n    const algebraicType = builder.typeBuilder.algebraicType;\n    if (ScheduleAt.isScheduleAt(algebraicType)) {\n      scheduleAtCol = colIds.get(name)!;\n    }\n  }\n\n  // convert explicit multi‑column indexes coming from options.indexes\n  for (const indexOpts of userIndexes ?? []) {\n    const accessor = indexOpts.accessor;\n    if (typeof accessor !== 'string' || accessor.length === 0) {\n      const tableLabel = name ?? '<unnamed>';\n      const indexLabel = indexOpts.name ?? '<unnamed>';\n      throw new TypeError(\n        `Index '${indexLabel}' on table '${tableLabel}' must define a non-empty 'accessor'`\n      );\n    }\n    let algorithm: RawIndexAlgorithm;\n    switch (indexOpts.algorithm) {\n      case 'btree':\n        algorithm = {\n          tag: 'BTree',\n          value: indexOpts.columns.map(c => colIds.get(c)!),\n        };\n        break;\n      case 'hash':\n        algorithm = {\n          tag: 'Hash',\n          value: indexOpts.columns.map(c => colIds.get(c)!),\n        };\n        break;\n      case 'direct':","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-typescript/src/lib/table.ts#L424-L460","documentation":"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.","triggerScenarios":"Declaring a table with indexes: [{ name: 'by_email', algorithm: 'btree', columns: ['email'] }] - no accessor field - or accessor: ''.","commonSituations":"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.","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"],"exampleFix":"// before\nindexes: [{ name: 'by_email', algorithm: 'btree', columns: ['email'] }]\n\n// after\nindexes: [{ name: 'by_email', accessor: 'byEmail', algorithm: 'btree', columns: ['email'] }]","handlingStrategy":"validation","validationCode":"function validateIndexOptions(indexes: Array<{ name?: string; accessor?: unknown }>): void {\n  for (const idx of indexes) {\n    if (typeof idx.accessor !== 'string' || idx.accessor.length === 0) {\n      throw new TypeError(`index '${idx.name ?? '<unnamed>'}' must define a non-empty 'accessor'`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["schema","index","table-definition","spacetimedb","validation"],"backgroundTag":"index-accessor-missing","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}