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

  1. Alias at least one of the conflicting tables: `users.as('manager')` and join that aliased instance.
  2. Rename the subquery alias so it doesn't collide with an existing join or the FROM table.
  3. 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

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


AI-assisted analysis of drizzle-team/drizzle-orm@b7862528fd (2026-08-03). Data as JSON: /data/errors/f56c84421e1fb041.json. Report an issue: GitHub.