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 in createJoin (select.ts:228) when adding a join whose resolved table alias already exists in this.config.joins. Drizzle requires each joined source to have a unique alias because field resolution and nullability tracking depend on alias uniqueness.
Source
Thrown at drizzle-orm/src/singlestore-core/query-builders/select.ts:228
TIsLateral extends (TJoinType extends 'full' | 'right' ? false : boolean),
>(
joinType: TJoinType,
lateral: TIsLateral,
): 'cross' extends TJoinType ? SingleStoreCrossJoinFn<this, TDynamic, TIsLateral>
: SingleStoreJoinFn<this, TDynamic, TJoinType, TIsLateral>
{
return (
table: SingleStoreTable | Subquery | SQL, // | SingleStoreViewBase
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 at least one of the conflicting tables: `users.as('manager')` and join that aliased instance.
- Rename the subquery alias so it doesn't collide with an existing join or the FROM table.
- If you truly need the same table twice, give each occurrence a distinct `.as(...)`.
Example fix
// before
await db.select()
.from(users)
.leftJoin(pets, eq(users.id, pets.ownerId))
.leftJoin(pets, eq(users.id, pets.secondaryOwnerId));
// after
const secondaryPets = pets.as('secondaryPets');
await db.select()
.from(users)
.leftJoin(pets, eq(users.id, pets.ownerId))
.leftJoin(secondaryPets, eq(users.id, secondaryPets.secondaryOwnerId)); Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueAliases(joins) {
const seen = new Set();
for (const j of joins) {
if (j.alias && seen.has(j.alias)) throw new Error(`Duplicate join alias ${j.alias}`);
if (j.alias) seen.add(j.alias);
}
} Type guard
function areJoinAliasesUnique(joins) {
const aliases = joins.map((j) => j.alias).filter(Boolean);
return new Set(aliases).size === aliases.length;
} Prevention
- Alias self-joined tables with .as('role').
- When joining the same table twice, give each a distinct alias.
- Audit join lists after copy-pasting clauses.
When it happens
Trigger: Joining the same table twice without aliasing the second instance via `.as('alias')`; joining a table whose name collides with the FROM table's name; joining two subqueries both aliased to the same string.
Common situations: Self-joins (e.g. users joined to users for manager relationships) without an alias; copy-pasting a join clause; joining a table and a subquery that derive the same alias.
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/f56c84421e1fb041.json.
Report an issue: GitHub.