drizzle-team/drizzle-orm · error · Error
Alias "${tableName}" is already used in this query
Error message
Alias "${tableName}" is already used in this query What it means
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.
Source
Thrown at drizzle-orm/src/sqlite-core/query-builders/select.ts:223
}
private createJoin<TJoinType extends JoinType>(
joinType: TJoinType,
): 'cross' extends TJoinType ? SQLiteSelectCrossJoinFn<this, TDynamic>
: SQLiteSelectJoinFn<this, TDynamic, TJoinType>
{
return (
table: SQLiteTable | Subquery | SQLiteViewBase | SQL,
on?: ((aliases: TSelection) => SQL | undefined) | SQL | undefined,
) => {
const baseTableName = this.tableName;
const tableName = getTableLikeName(table);
// store all tables used in a query
for (const item of extractUsedTable(table)) this.usedTables.add(item);
if (typeof tableName === 'string' && this.config.joins?.some((join) => join.alias === tableName)) {
throw new Error(`Alias "${tableName}" is already used in this query`);
}
if (!this.isPartialSelect) {
// 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
if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === 'string') {
this.config.fields = {
[baseTableName]: this.config.fields,
};
}
if (typeof tableName === 'string' && !is(table, SQL)) {
const selection = is(table, Subquery)
? table._.selectedFields
: is(table, View)
? table[ViewBaseConfig].selectedFields
: table[Table.Symbol.Columns];
this.config.fields[tableName] = selection;
}
}View on GitHub (pinned to b7862528fd)
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.
Example fix
// before
db.select().from(posts)
.leftJoin(posts, eq(posts.id, posts.parentId)); // 'posts' used twice
// after
const replies = posts.as('replies');
db.select().from(posts)
.leftJoin(replies, eq(replies.parentId, posts.id)); Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueAlias(joins, alias) {
if (joins.some((j) => j.alias === alias)) {
throw new Error(`Alias ${alias} already used`);
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Alias "${tableName}" is already used in this query
- Alias "${tableName}" is already used in this query
- Your "${f.path.join('->')}" field references a column "${tab
- Alias "${tableName}" is already used in this query
- Alias "${tableName}" is already used in this query
AI-assisted analysis of drizzle-team/drizzle-orm@b7862528fd (2026-08-03).
Data as JSON: /data/errors/e0e688f48733252a.json.
Report an issue: GitHub.