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
Inside PgSelect's join creation (select.ts:248), Drizzle tracks every join alias and throws if a new join reuses an alias already present in config.joins. Postgres requires unique correlation names within a query, so duplicate aliases would produce ambiguous SQL; the check is performed by comparing getTableLikeName(table) against existing join aliases.
Source
Thrown at drizzle-orm/src/pg-core/query-builders/select.ts:249
TIsLateral extends (TJoinType extends 'full' | 'right' ? false : boolean),
>(
joinType: TJoinType,
lateral: TIsLateral,
): 'cross' extends TJoinType ? PgSelectCrossJoinFn<this, TDynamic, TIsLateral>
: PgSelectJoinFn<this, TDynamic, TJoinType, TIsLateral>
{
return ((
table: TIsLateral extends true ? Subquery | SQL : PgTable | Subquery | PgViewBase | 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 occurrence using sql.alias(...) or a subquery with a distinct alias.
- For self-joins, create two aliased subqueries or use sql`<table> AS <alias>`.
- Track used aliases in dynamic builders and generate unique names (e.g., `${name}_${i}`).
Example fix
// before
db.select().from(users)
.leftJoin(posts, eq(posts.userId, users.id))
.leftJoin(posts, eq(posts.authorId, users.id)); // alias 'posts' reused
// after
const p1 = db.$with('p1').as(db.select().from(posts));
const p2 = db.$with('p2').as(db.select().from(posts));
db.with(p1, p2).select().from(users)
.leftJoin(p1, eq(p1.userId, users.id))
.leftJoin(p2, eq(p2.authorId, users.id)); Defensive patterns
Strategy: validation
Validate before calling
function buildJoin(db: Db, base: PgTable, joins: { table: PgTable | SQL; on: SQL }[]) {
const used = new Set<string>([base[Table.Symbol.Name]]);
let q = db.select().from(base);
for (const j of joins) {
const alias = getTableLikeName(j.table);
if (typeof alias === 'string' && used.has(alias)) {
throw new Error(`Alias "${alias}" already used; provide a unique alias`);
}
if (typeof alias === 'string') used.add(alias);
q = q.leftJoin(j.table, j.on);
}
return q;
} Type guard
function isUniqueAlias(alias: string, used: Set<string>): boolean {
return !used.has(alias);
} Prevention
- Track used aliases in dynamic query builders.
- Alias self-joins and repeated tables via subqueries ($with/as).
- Use distinct, descriptive alias names per join.
When it happens
Trigger: Joining the same table twice without aliasing one: db.select().from(t).leftJoin(t2, ...).leftJoin(t2, ...); or joining a subquery whose derived alias collides with an earlier table/join alias. Also triggered by aliasing two subqueries to the same name.
Common situations: Self-joins where both sides use the default table name; reusing a table reference variable in multiple joins; dynamic query builders that append joins without tracking used aliases.
Related errors
- Alias "${tableName}" is already used in this query
- Alias "${tableName}" is already used in this query
- Set operator error (union / intersect / except): selected fi
- Your "${f.path.join('->')}" field references a column "${tab
- Set operator error (union / intersect / except): selected fi
AI-assisted analysis of drizzle-team/drizzle-orm@b7862528fd (2026-08-03).
Data as JSON: /data/errors/036884c29322c7a7.json.
Report an issue: GitHub.