{"record":{"id":"2a08e5f63e7f566c","repo":"clockworklabs/SpacetimeDB","slug":"schema-entry-accname-must-be-a-table-or-a-sub","errorCode":null,"errorMessage":"schema entry '${accName}' must be a table or a submodule namespace object","messagePattern":"schema entry '(.+?)' must be a table or a submodule namespace object","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/server/schema.ts","lineNumber":868,"sourceCode":"    }\n\n    const tableSchemas: Record<string, UntypedTableDef> = {};\n    for (const [accName, entry] of Object.entries(entries)) {\n      if (entry instanceof Schema) {\n        throw new TypeError(\n          `schema entry '${accName}' looks like a default import; use \\`import * as ${accName} from '...'\\` so the submodule can see the library's named reducer exports.`\n        );\n      }\n      if (isSubmoduleNamespace(entry)) {\n        const { rawDef, dispatch } =\n          entry.default.buildSubmoduleDispatch(entry);\n        dispatch.namespace = accName;\n        ctx.addSubmodule({ namespace: accName, module: rawDef });\n        ctx.submoduleDispatchInfos.push(dispatch);\n        continue;\n      }\n      if (!isUntypedTableSchema(entry)) {\n        throw new TypeError(\n          `schema entry '${accName}' must be a table or a submodule namespace object`\n        );\n      }\n\n      const table = entry;\n      const tableDef = table.tableDef(ctx, accName);\n      tableSchemas[accName] = tableToSchema(accName, table, tableDef);\n      const tableSourceNames = ctx.tableSourceNames.get(table);\n      if (tableSourceNames === undefined) {\n        ctx.tableSourceNames.set(table, [tableDef.sourceName]);\n      } else {\n        tableSourceNames.push(tableDef.sourceName);\n      }\n      ctx.moduleDef.tables.push(tableDef);\n      if (table.schedule) {\n        ctx.pendingSchedules.push({\n          table,\n          tableName: tableDef.sourceName,","sourceCodeStart":850,"sourceCodeEnd":886,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/bindings-typescript/src/server/schema.ts#L850-L886","documentation":"`schema()` accepts an entries object whose values must each be either a table definition (created by `table()`) or a submodule namespace object (detected by `isSubmoduleNamespace`). Anything else — numbers, strings, plain objects, arrays, class instances, the row-type object — fails `isUntypedTableSchema` and throws `schema entry '<name>' must be a table or a submodule namespace object`, naming the offending key.","triggerScenarios":"`schema({ users: 42 })`, `schema({ row: usersRowTypeObj })` (passing the row type instead of the table), `schema({ cfg: { tick: 1 } })` (config object), or a namespace import that resolved to a non-module value; a schema entry whose table came from a different, incompatible bindings version.","commonSituations":"Passing `.rowType` or a raw row builder instead of the table handle; leaving a leftover config entry inside the schema object; version skew between a library compiled against an older bindings package so its table objects fail the structural check.","solutions":["Check the entry named in the message: replace it with the value returned by `table(...)`.","For submodules, use a real namespace import (`import * as lib from 'lib'`) — that object is what `isSubmoduleNamespace` detects.","Move config/constants out of the schema entries object.","If the value came from a dependency, align the dependency's bindings version with the app's (`pnpm` dedupe / matching `@clockworklabs/spacetime-sdk` versions)."],"exampleFix":"// before\nexport default schema({\n  users: usersTable,\n  settings: { tickRate: 5 }, // plain object\n});\n\n// after\nexport default schema({\n  users: usersTable,\n  settingsTable, // a real table() handle\n});","handlingStrategy":"validation","validationCode":"// Validate entries are tables or namespace objects before schema().\nfunction assertSchemaEntries(entries: Record<string, unknown>) {\n  for (const [name, entry] of Object.entries(entries)) {\n    const looksLikeTable = typeof entry === 'object' && entry !== null && 'tableDef' in entry;\n    const looksLikeNamespace = typeof entry === 'object' && entry !== null && 'default' in entry;\n    if (!looksLikeTable && !looksLikeNamespace) {\n      throw new Error(`schema entry '${name}' is neither a table() handle nor a namespace import`);\n    }\n  }\n}","typeGuard":"function isTableHandle(x: unknown): boolean {\n  return typeof x === 'object' && x !== null && typeof (x as any).tableDef === 'function';\n}","tryCatchPattern":null,"preventionTips":["Only put `table()` return values and namespace imports into the schema entries object.","Never pass `.rowType` or config objects; double-check each entry after refactors.","Align bindings versions across workspace packages so structural checks on table objects keep passing."],"tags":["schema","validation","build-time","spacetimedb","typescript"],"backgroundTag":"invalid-schema-entry","analyzedSha":"eddf9f5014579a50d4b67630e28b6e15cad9c4af","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}