n8n-io/n8n · error · TypeORMError

Cannot create relation id map for a single value because rel

Error message

Cannot create relation id map for a single value because relation contains multiple referenced columns.

What it means

Thrown by RelationMetadata.ensureRelationIdMap when a scalar id is passed for a relation whose join columns reference more than one column (composite foreign key). The method picks joinColumns (owning side) or inverseRelation.joinColumns, derives referencedColumns, and refuses when that array length exceeds 1 because a single value cannot populate composite FK columns.

Source

Thrown at packages/@n8n/typeorm/src/metadata/RelationMetadata.ts:378

		// console.log("entity", entity);
		// console.log("referencedColumns", referencedColumns);
		return EntityMetadata.getValueMap(entity, referencedColumns);
	}

	/**
	 * Ensures that given object is an entity id map.
	 * If given id is an object then it means its already id map.
	 * If given id isn't an object then it means its a value of the id column
	 * and it creates a new id map with this value and name of the primary column.
	 */
	ensureRelationIdMap(id: any): ObjectLiteral {
		if (ObjectUtils.isObject(id)) return id;

		const joinColumns = this.isOwning ? this.joinColumns : this.inverseRelation!.joinColumns;
		const referencedColumns = joinColumns.map((joinColumn) => joinColumn.referencedColumn!);

		if (referencedColumns.length > 1)
			throw new TypeORMError(
				`Cannot create relation id map for a single value because relation contains multiple referenced columns.`,
			);

		return referencedColumns[0].createValueMap(id);
	}

	/**
	 * Extracts column value from the given entity.
	 * If column is in embedded (or recursive embedded) it extracts its value from there.
	 */
	getEntityValue(
		entity: ObjectLiteral,
		getLazyRelationsPromiseValue: boolean = false,
	): any | undefined {
		if (entity === null || entity === undefined) return undefined;
		// extract column value from embeddeds of entity if column is in embedded
		if (this.embeddedMetadata) {
			// example: post[data][information][counters].id where "data", "information" and "counters" are embeddeds

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass an object id-map keyed by the referenced column names instead of a scalar.
  2. If the relation truly has a single FK, audit @JoinColumn arrays and ensure only one join column is declared.
  3. Wrap relation lookups behind a helper that always emits the composite id-map shape.

Example fix

// before
await postRepo.createQueryBuilder().relation('category').of(5).loadOne();
// where category uses composite FK

// after
await postRepo
  .createQueryBuilder()
  .relation('category')
  .of({ tenantId: 1, id: 5 })
  .loadOne();
Defensive patterns

Strategy: type-guard

Validate before calling

function relationHasCompositeFk(
  metadata: EntityMetadata,
  relationPath: string,
): boolean {
  const rel = metadata.relations.find((r) => r.propertyPath === relationPath);
  if (!rel) return false;
  const joinCols = rel.isOwning ? rel.joinColumns : rel.inverseRelation?.joinColumns ?? [];
  return joinCols.length > 1;
}

// pass an object id when true
const idShape = relationHasCompositeFk(meta, 'category')
  ? { tenantId, id }
  : categoryId;

Type guard

type ScalarId = string | number;
type IdMap = Record<string, unknown>;
function isScalarId(id: unknown): id is ScalarId {
  return typeof id === 'string' || typeof id === 'number';
}
function isCompositeFk(relation: RelationMetadata): boolean {
  const cols = relation.isOwning
    ? relation.joinColumns
    : relation.inverseRelation?.joinColumns ?? [];
  return cols.length > 1;
}

Try / catch

try {
  await postRepo.createQueryBuilder().relation('category').of(id).loadOne();
} catch (err) {
  if (err.message.includes('multiple referenced columns')) {
    // re-issue with an id-map object
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a relation-id helper or repository method that funnels a raw scalar through ensureRelationIdMap on a relation backed by a composite FK (e.g. @ManyToOne joining on two columns).

Common situations: Multi-tenant schemas where relations use composite keys; converting a single-column FK to a composite one without updating callers that pass scalar ids.

Related errors


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