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(null) method instead.

What it means

RelationQueryBuilder.remove supports only many-to-many and one-to-many. If the resolved relation is many-to-one or one-to-one it throws TypeORMError, but — unlike .add — points the developer to .set(null) because clearing a single-valued FK is the correct unbind operation for those relation types. Note the message string still says 'Add operation' which is a copy-paste bug in the upstream message.

Source

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

	 * Removes (unbinds) given value from 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 remove(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(null) method instead.`,
			);

		const remover = new RelationRemover(this, this.expressionMap);
		return remover.remove(value);
	}

	/**
	 * Adds (binds) and removes (unbinds) given values to/from 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 addAndRemove(added: any | any[], removed: any | any[]): Promise<void> {
		await this.remove(removed);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Use .set(null) to unbind a single-valued relation: qb.relation(User,'profile').of(u).set(null).
  2. Confirm the decorator on relation.propertyPath to be sure which API applies.
  3. If the intent really is collection removal, switch the relation target to the collection side.

Example fix

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

Strategy: type-guard

Validate before calling

function supportsRemove(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).remove(profile) on a @OneToOne; .remove() on a @ManyToOne like User.organization.

Common situations: Decorator confusion; developer assumes .remove works on all relations; intent is actually to null out an FK but the wrong API was chosen.

Related errors


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