{"id":"e0e688f48733252a","repo":"drizzle-team/drizzle-orm","slug":"alias-tablename-is-already-used-in-this-query-e0e688","errorCode":null,"errorMessage":"Alias \"${tableName}\" is already used in this query","messagePattern":"Alias \"(.+?)\" is already used in this query","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"drizzle-orm/src/sqlite-core/query-builders/select.ts","lineNumber":223,"sourceCode":"\t}\n\n\tprivate createJoin<TJoinType extends JoinType>(\n\t\tjoinType: TJoinType,\n\t): 'cross' extends TJoinType ? SQLiteSelectCrossJoinFn<this, TDynamic>\n\t\t: SQLiteSelectJoinFn<this, TDynamic, TJoinType>\n\t{\n\t\treturn (\n\t\t\ttable: SQLiteTable | Subquery | SQLiteViewBase | SQL,\n\t\t\ton?: ((aliases: TSelection) => SQL | undefined) | SQL | undefined,\n\t\t) => {\n\t\t\tconst baseTableName = this.tableName;\n\t\t\tconst tableName = getTableLikeName(table);\n\n\t\t\t// store all tables used in a query\n\t\t\tfor (const item of extractUsedTable(table)) this.usedTables.add(item);\n\n\t\t\tif (typeof tableName === 'string' && this.config.joins?.some((join) => join.alias === tableName)) {\n\t\t\t\tthrow new Error(`Alias \"${tableName}\" is already used in this query`);\n\t\t\t}\n\n\t\t\tif (!this.isPartialSelect) {\n\t\t\t\t// If this is the first join and this is not a partial select and we're not selecting from raw SQL, \"move\" the fields from the main table to the nested object\n\t\t\t\tif (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === 'string') {\n\t\t\t\t\tthis.config.fields = {\n\t\t\t\t\t\t[baseTableName]: this.config.fields,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tif (typeof tableName === 'string' && !is(table, SQL)) {\n\t\t\t\t\tconst selection = is(table, Subquery)\n\t\t\t\t\t\t? table._.selectedFields\n\t\t\t\t\t\t: is(table, View)\n\t\t\t\t\t\t? table[ViewBaseConfig].selectedFields\n\t\t\t\t\t\t: table[Table.Symbol.Columns];\n\t\t\t\t\tthis.config.fields[tableName] = selection;\n\t\t\t\t}\n\t\t\t}","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/drizzle-team/drizzle-orm/blob/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10/drizzle-orm/src/sqlite-core/query-builders/select.ts#L205-L241","documentation":"Thrown when joining a table in a SQLite SELECT (select.ts:223) whose alias (the table's own name or an alias() string) already matches an alias registered by a previous join in the same query. Drizzle requires unique aliases so the generated SQL is unambiguous.","triggerScenarios":"Joining the same table twice without aliasing one of them; joining two tables that share an alias string; .leftJoin(t2.as('x')) after already joining something aliased 'x'. The base FROM table's name also counts as a used alias.","commonSituations":"Self-joins (post↔post) without .as(); joining a table and a view/subquery that share a name; alias collisions after renaming; copy-pasting a join block.","solutions":["Alias the second instance: table.as('alias2') and use that alias throughout the join and selection.","Rename a conflicting subquery alias to something unique.","Audit the joins array for duplicate alias strings before building."],"exampleFix":"// before\ndb.select().from(posts)\n  .leftJoin(posts, eq(posts.id, posts.parentId)); // 'posts' used twice\n\n// after\nconst replies = posts.as('replies');\ndb.select().from(posts)\n  .leftJoin(replies, eq(replies.parentId, posts.id));","handlingStrategy":"validation","validationCode":"function assertUniqueAlias(joins, alias) {\n  if (joins.some((j) => j.alias === alias)) {\n    throw new Error(`Alias ${alias} already used`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["For self-joins, always alias the second instance with .as('name').","Keep a set of used aliases when building joins dynamically.","Prefer descriptive unique aliases over reusing table names."],"tags":["select","joins","alias","self-join"],"analyzedSha":"b7862528fd8fc39bc2653a6c18dad7c1f4e68d10","analyzedAt":"2026-08-03T18:11:14.318Z","schemaVersion":2}