{"record":{"id":"20dcddbc361fa50f","repo":"hasura/graphql-engine","slug":"tablename-join-is-not-a-valid-table","errorCode":null,"errorMessage":"${tableName.join('.')} is not a valid table","messagePattern":"(.+?) is not a valid table","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"dc-agents/sqlite/src/query.ts","lineNumber":76,"sourceCode":" *\n * @param identifier: Unescaped name. E.g. 'Alb\"um'\n * @returns Escaped name. E.g. '\"Alb\\\"um\"'\n */\nexport function escapeIdentifier(identifier: string): string {\n  // TODO: Review this function since the current implementation is off the cuff.\n  const result = identifier.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n  return `\"${result}\"`;\n}\n\n/**\n * Throw an exception if the tableName has invalid number of prefix components.\n *\n * @param tableName: Unescaped table name. E.g. 'Alb\"um'\n * @returns tableName\n */\nfunction validateTableName(tableName: TableName): TableName {\n  if (tableName.length <= 2 && tableName.length > 0) return tableName;\n  else throw new Error(`${tableName.join('.')} is not a valid table`);\n}\n\n/**\n * @param ts\n * @returns last section of a qualified table array. E.g. [a,b] -> [b]\n */\nexport function getTableNameSansSchema(ts: Array<string>): Array<string> {\n  return [ts[ts.length - 1]];\n}\n\n/**\n *\n * @param tableName: Unescaped table name. E.g. 'Alb\"um'\n * @returns Escaped table name. E.g. '\"Alb\\\"um\"'\n */\nexport function escapeTableName(tableName: TableName): string {\n  return validateTableName(tableName).map(escapeIdentifier).join('.');\n}","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/dc-agents/sqlite/src/query.ts#L58-L94","documentation":"validateTableName in the SQLite agent's query builder rejects table names whose parts are 1 or 2 characters long. The logic (`tableName.length <= 2 && tableName.length > 0`) means any qualified name array with fewer than 3 parts throws `${name} is not a valid table`. This effectively requires fully-qualified names and is a validation quirk of the agent's SQL escaping pipeline.","triggerScenarios":"Calling query/explain with `target.name` arrays like `['users']` (length 1) or `['main','users']` (length 2); any code path reaching escapeTableName/generateTableAlias with a short name array.","commonSituations":"Clients sending bare unqualified table names; schema metadata that stores unqualified names; refactors that changed the expected name-array shape. Note: for interpolated targets only `[target.id]` is passed, which is length 1 — so this is only thrown on paths where a proper qualified name is expected.","solutions":["Send fully-qualified table names with at least 3 parts, e.g. `['database','schema','table']` as the agent's naming convention expects","Check how tables are listed in the agent's schema response and mirror that name array exactly","If your table legitimately has a short name, qualify it with database/schema prefixes"],"exampleFix":"// before\n{ target: { type: 'table', name: ['album'] } }\n// after\n{ target: { type: 'table', name: ['main', 'public', 'album'] } }","handlingStrategy":"validation","validationCode":"const isValidTableName = (n: string[]) => n.length >= 3 || n.length === 0;","typeGuard":"const isValidTableName = (n: string[]): n is string[] => !(n.length > 0 && n.length <= 2);","tryCatchPattern":null,"preventionTips":["Always send fully-qualified name arrays as the agent's schema endpoint reports them","Never hand-construct short name arrays for tables (single-part arrays are reserved for interpolated ids)"],"tags":["sqlite","table-name","validation","query-builder"],"backgroundTag":"invalid-identifier","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}