n8n-io/n8n · error · TypeORMError

Entity to work with is not specified!

Error message

Entity to work with is not specified!

What it means

The relationMetadata getter on QueryExpressionMap requires a mainAlias (the entity the builder was created from). It throws TypeORMError when mainAlias is falsy, which happens when createQueryBuilder() was called without a target entity (e.g. raw query builder) but a relation API (.relation(...), .of(...), .set/.add/.remove) was then used.

Source

Thrown at packages/@n8n/typeorm/src/query-builder/QueryExpressionMap.ts:454

		if (!alias)
			throw new TypeORMError(`"${aliasName}" alias was not found. Maybe you forgot to join it?`);

		return alias;
	}

	findColumnByAliasExpression(aliasExpression: string): ColumnMetadata | undefined {
		const [aliasName, propertyPath] = aliasExpression.split('.');
		const alias = this.findAliasByName(aliasName);
		return alias.metadata.findColumnWithPropertyName(propertyPath);
	}

	/**
	 * Gets relation metadata of the relation this query builder works with.
	 *
	 * todo: add proper exceptions
	 */
	get relationMetadata(): RelationMetadata {
		if (!this.mainAlias) throw new TypeORMError(`Entity to work with is not specified!`); // todo: better message

		const relationMetadata = this.mainAlias.metadata.findRelationWithPropertyPath(
			this.relationPropertyPath,
		);
		if (!relationMetadata)
			throw new TypeORMError(
				`Relation ${this.relationPropertyPath} was not found in entity ${this.mainAlias.name}`,
			); // todo: better message

		return relationMetadata;
	}

	/**
	 * Copies all properties of the current QueryExpressionMap into a new one.
	 * Useful when QueryBuilder needs to create a copy of itself.
	 */
	clone(): QueryExpressionMap {
		const map = new QueryExpressionMap(this.connection);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Construct the relation builder from an entity-bound builder: dataSource.createQueryBuilder(User).relation('projects').of(user).add(project).
  2. If you only need raw SQL, drop the .relation() call and use .insert().into(...)/.update() directly.
  3. Ensure the helper that hands out the builder always seeds it with an entity target.

Example fix

// before
await dataSource.createQueryBuilder().relation('User.projects').of(user).add(p);
// after
await dataSource.createQueryBuilder(User).relation('projects').of(user).add(p);
Defensive patterns

Strategy: type-guard

Validate before calling

import { SelectQueryBuilder } from 'typeorm';

function ensureMainAlias<T>(qb: SelectQueryBuilder<T>): asserts qb is SelectQueryBuilder<T> {
  if (!qb.expressionMap.mainAlias)
    throw new Error('Builder has no main entity alias; create it via createQueryBuilder(Entity).');
}

Type guard

function hasMainAlias(qb: SelectQueryBuilder<any>): boolean {
  return !!qb.expressionMap.mainAlias;
}

Prevention

When it happens

Trigger: Calling dataSource.createQueryBuilder().relation('User.projects') without first binding an entity via .from(User) or createQueryBuilder(User). Using a relation builder off a raw SQL builder returned by createQueryBuilder<SomeShape>().

Common situations: Migrating a raw query to the relation API; helper functions that accept a generic SelectQueryBuilder and accidentally invoke relation operations; chaining .relation() on a builder obtained from manager.createQueryBuilder() with no generic.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/e7b7673f573e5572. Report an issue: GitHub.