{"record":{"id":"26384cc1c255c710","repo":"clockworklabs/SpacetimeDB","slug":"cannot-extract-table-name-from-query","errorCode":null,"errorMessage":"Cannot extract table name from query","messagePattern":"Cannot extract table name from query","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/bindings-typescript/src/lib/query.ts","lineNumber":1046,"sourceCode":"export function toComparableValue(value: any): any {\n  // Handle `ConnectionId` and `Identity`.\n  if (isHexSerializableLike(value)) {\n    return value.toHexString();\n  }\n  if (isTimestampLike(value)) {\n    return value.__timestamp_micros_since_unix_epoch__;\n  }\n  return value;\n}\n\n/**\n * Extract the table name from a query builder expression.\n */\nexport function getQueryTableName(query: any): string {\n  if (query.table) return query.table.name; // FromBuilder\n  if (query.name) return query.name; // TableRefImpl\n  if (query.sourceQuery) return query.sourceQuery.table.name; // SemijoinImpl (source table)\n  throw new Error('Cannot extract table name from query');\n}\n\n/**\n * Extract the accessor name from a query builder expression.\n */\nexport function getQueryAccessorName(query: any): string {\n  if (query.table) return query.table.accessorName; // FromBuilder\n  if (query.accessorName) return query.accessorName; // TableRefImpl\n  if (query.sourceQuery) return query.sourceQuery.table.accessorName; // SemijoinImpl\n  throw new Error('Cannot extract accessor name from query');\n}\n\n/**\n * Extract the BooleanExpr from a query builder, if any.\n */\nexport function getQueryWhereClause(query: any): BooleanExpr<any> | undefined {\n  if (query.whereClause) return query.whereClause; // FromBuilder\n  return undefined; // TableRefImpl has no where clause","sourceCodeStart":1028,"sourceCodeEnd":1064,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-typescript/src/lib/query.ts#L1028-L1064","documentation":"getQueryTableName(query) duck-types the three known builder shapes - FromBuilder (.table), TableRefImpl (.name), and SemijoinImpl (.sourceQuery) - and throws when the argument matches none. It is the exported helper that bridges arbitrary query-builder expressions to table names (sibling of getQueryAccessorName used by the framework hooks).","triggerScenarios":"Passing anything that is not a table ref, a .where(...) chain, or a semijoin result - e.g. the Query returned by .build(), a plain object literal, or a test mock - into getQueryTableName or into internal plumbing that calls it.","commonSituations":"New builder kinds (other joins, aggregations) the extractor has not learned; tests mocking builders as plain objects; re-feeding a built Query back into an API that expects the builder.","solutions":["Pass the original builder expression (tables.user or tables.user.where(...)) instead of a built Query or plain object","If you author a custom builder, expose one of the recognized shapes (.table, .name, or .sourceQuery)","Extend getQueryTableName to recognize the new query kind"],"exampleFix":"// before\nconst name = getQueryTableName(tables.user.build()); // built Query -> throws\n\n// after\nconst name = getQueryTableName(tables.user);","handlingStrategy":"type-guard","validationCode":"function isKnownQueryBuilder(q: unknown): boolean {\n  const c = q as Record<string, unknown> | null | undefined;\n  return Boolean(c && (c.table || c.name || c.sourceQuery));\n}","typeGuard":"type KnownQueryBuilder = { table: unknown } | { name: unknown } | { sourceQuery: unknown };\nfunction isKnownQueryBuilder(q: unknown): q is KnownQueryBuilder {\n  const c = q as Record<string, unknown> | null | undefined;\n  return Boolean(c && (c.table || c.name || c.sourceQuery));\n}","tryCatchPattern":null,"preventionTips":["Always pass builder expressions (table ref, .where chain, semijoin), never .build() results or literals","Type hook wrappers to accept Query<TableDef> so plain objects are rejected at compile time","When adding a new builder shape, update the extractors or expose a recognized field"],"tags":["query-builder","spacetimedb","typescript","duck-typing"],"backgroundTag":"unrecognized-query-shape","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}