n8n-io/n8n · error · TypeORMError

Add operation is only supported for many-to-many and one-to-

Error message

Add operation is only supported for many-to-many and one-to-many relations. However given "${relation.propertyPath}" has ${relation.relationType} relation. Use .set() method instead.

What it means

RelationQueryBuilder.add is the inverse of .set: it only operates on collection relations (many-to-many, one-to-many). If the resolved relation is many-to-one or one-to-one it throws TypeORMError directing the developer to .set() instead, because adding to a single-valued FK is not a collection mutation.

Source

Thrown at packages/@n8n/typeorm/src/query-builder/RelationQueryBuilder.ts:94

	 * Adds (binds) given value to entity relation.
	 * Value can be entity, entity id or entity id map (if entity has composite ids).
	 * Value also can be array of entities, array of entity ids or array of entity id maps (if entity has composite ids).
	 * Works only for many-to-many and one-to-many relations.
	 * For many-to-one and one-to-one use #set method instead.
	 */
	async add(value: any | any[]): Promise<void> {
		if (Array.isArray(value) && value.length === 0) return;

		const relation = this.expressionMap.relationMetadata;

		if (!this.expressionMap.of)
			// todo: move this check before relation query builder creation?
			throw new TypeORMError(
				`Entity whose relation needs to be set is not set. Use .of method to define whose relation you want to set.`,
			);

		if (relation.isManyToOne || relation.isOneToOne)
			throw new TypeORMError(
				`Add operation is only supported for many-to-many and one-to-many relations. ` +
					`However given "${relation.propertyPath}" has ${relation.relationType} relation. ` +
					`Use .set() method instead.`,
			);

		// if there are multiple join columns then user must send id map as "value" argument. check if he really did it
		if (
			relation.joinColumns &&
			relation.joinColumns.length > 1 &&
			(!ObjectUtils.isObject(value) || Object.keys(value).length < relation.joinColumns.length)
		)
			throw new TypeORMError(
				`Value to be set into the relation must be a map of relation ids, for example: .set({ firstName: "...", lastName: "..." })`,
			);

		const updater = new RelationUpdater(this, this.expressionMap);
		return updater.update(value);
	}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Use .set() for single-valued relations: qb.relation(User,'profile').of(u).set(profile).
  2. Confirm the decorator on relation.propertyPath; if it really is a collection, the relation metadata may be on the wrong side.
  3. For replacing a single-valued FK to null, use .set(null).

Example fix

// before
await qb.relation(User,'profile').of(u).add(profile); // profile is one-to-one
// after
await qb.relation(User,'profile').of(u).set(profile);
Defensive patterns

Strategy: type-guard

Validate before calling

function supportsAdd(ds: DataSource, target: Function, path: string): boolean {
  const r = ds.getMetadata(target).findRelationWithPropertyPath(path);
  return !!r && (r.isManyToMany || r.isOneToMany);
}

Type guard

function isCollectionRelation(ds: DataSource, target: Function, path: string): boolean {
  const r = ds.getMetadata(target).findRelationWithPropertyPath(path);
  return !!r && (r.relationType === 'many-to-many' || r.relationType === 'one-to-many');
}

Prevention

When it happens

Trigger: qb.relation(User,'profile').of(u).add(profile) where profile is @OneToOne; .add() on a @ManyToOne like User.organization.

Common situations: Developer assumes all relations support .add(); decorator confusion; copy-paste from a tags relation onto a profile relation.

Related errors


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