n8n-io/n8n · error · TypeORMError

Relation ${this.relationPropertyPath} was not found in entit

Error message

Relation ${this.relationPropertyPath} was not found in entity ${this.mainAlias.name}

What it means

After mainAlias is confirmed present, the relationMetadata getter walks the entity metadata via findRelationWithPropertyPath(this.relationPropertyPath). If the relation path supplied to .relation() does not correspond to any @OneToMany/@ManyToOne/@ManyToMany/@OneToOne on the entity, it throws TypeORMError naming the bad path and the entity alias.

Source

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

	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);
		map.queryType = this.queryType;
		map.selects = this.selects.map((select) => select);
		map.maxExecutionTime = this.maxExecutionTime;
		map.selectDistinct = this.selectDistinct;
		map.selectDistinctOn = this.selectDistinctOn;
		this.aliases.forEach((alias) => map.aliases.push(new Alias(alias)));

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Open the entity class for mainAlias.metadata.targetName and confirm the exact @*ToOne decorator property name; correct the .relation() argument.
  2. For nested paths, verify each segment is a relation (not a column) and that the relation is eager or already joined.
  3. If you meant a column not a relation, use .set() on a normal update builder instead of the relation API.

Example fix

// before
await qb.relation('User', 'gruops').of(user).add(g);
// after
await qb.relation('User', 'groups').of(user).add(g);
Defensive patterns

Strategy: validation

Validate before calling

import { DataSource } from 'typeorm';

function assertRelation(ds: DataSource, target: Function, path: string) {
  const meta = ds.getMetadata(target);
  if (!meta.findRelationWithPropertyPath(path))
    throw new Error(`'${path}' is not a relation on ${meta.targetName}`);
}

Type guard

function isRelationPath(ds: DataSource, target: Function, path: string): boolean {
  return !!ds.getMetadata(target).findRelationWithPropertyPath(path);
}

Prevention

When it happens

Trigger: qb.relation('User.gruops') typo; calling .relation() with a path that points at a plain @Column instead of a relation; referencing a relation that exists on the inverse entity but not on this side; deep path like 'a.b.c' where an intermediate segment is not a relation.

Common situations: Renaming a relation property in the entity without updating call sites; assuming a relation exists from both sides when only one side declares @ManyToOne; copy-paste between entities with different relation names.

Related errors


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