n8n-io/n8n · error · TypeORMError

Value to be set into the relation must be a map of relation

Error message

Value to be set into the relation must be a map of relation ids, for example: .set({ firstName: "...", lastName: "..." })

What it means

Inside RelationQueryBuilder.set, when relation.joinColumns.length > 1 (a composite FK), the value argument must be an object map with at least one key per join column. If ObjectUtils.isObject(value) is false or the key count is less than the join-column count, TypeORM throws TypeORMError showing the expected shape .set({ firstName:'...', lastName:'...' }).

Source

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

			// 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.isManyToMany || relation.isOneToMany)
			throw new TypeORMError(
				`Set operation is only supported for many-to-one and one-to-one relations. ` +
					`However given "${relation.propertyPath}" has ${relation.relationType} relation. ` +
					`Use .add() 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);
	}

	/**
	 * 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;

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass a map covering every referenced column: qb.relation(...).of(u).set({ tenantId, userId }).
  2. Inspect relation.joinColumns (or its referencedColumn.propertyName) at design time to enumerate the required keys.
  3. If the caller only has one id, fetch the related entity first and pass its composite id map.

Example fix

// before
qb.relation(Membership,'user').of(m).set(userId); // join uses (tenantId,userId)
// after
qb.relation(Membership,'user').of(m).set({ tenantId, userId });
Defensive patterns

Strategy: type-guard

Validate before calling

import { DataSource } from 'typeorm';

function compositeKeys(ds: DataSource, target: Function, path: string): string[] {
  const r = ds.getMetadata(target).findRelationWithPropertyPath(path)!;
  return r.joinColumns.map((c: any) => c.referencedColumn!.propertyName);
}
// const keys = compositeKeys(ds, Membership, 'user');
// if (Object.keys(value).length < keys.length) throw new Error(`need keys ${keys.join(',')}`);

Type guard

function isCompositeValueMap(value: unknown, keys: string[]): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && keys.every((k) => k in value);
}

Prevention

When it happens

Trigger: Calling .set(singleId) on a relation whose join uses composite keys (e.g. a relation keyed by (tenantId, userId)). Passing an array or a primitive where a partial-object map is required.

Common situations: Composite-key entities introduced after the call site was written; tenant-scoped tables where every relation carries tenantId in the FK; passing a bare id from a request param instead of {tenantId, userId}.

Related errors


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