n8n-io/n8n · error · TypeORMError

Cannot load entity because only one primary key was specifie

Error message

Cannot load entity because only one primary key was specified, however entity contains multiple primary keys

What it means

RelationQueryBuilder.loadMany accepts a scalar id in .of() only when the entity has a single primary key. When ObjectUtils.isObject(of) is false and metadata.hasMultiplePrimaryKeys is true, it cannot decide which PK the scalar refers to, so it throws TypeORMError rather than guessing.

Source

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

	/**
	 * Loads a single entity (relational) from the relation.
	 * You can also provide id of relational entity to filter by.
	 */
	async loadOne<T = any>(): Promise<T | undefined> {
		return this.loadMany<T>().then((results) => results[0]);
	}

	/**
	 * Loads many entities (relational) from the relation.
	 * You can also provide ids of relational entities to filter by.
	 */
	async loadMany<T = any>(): Promise<T[]> {
		let of = this.expressionMap.of;
		if (!ObjectUtils.isObject(of)) {
			const metadata = this.expressionMap.mainAlias!.metadata;
			if (metadata.hasMultiplePrimaryKeys)
				throw new TypeORMError(
					`Cannot load entity because only one primary key was specified, however entity contains multiple primary keys`,
				);

			of = metadata.primaryColumns[0].createValueMap(of);
		}

		return this.connection.relationLoader.load(
			this.expressionMap.relationMetadata,
			of,
			this.queryRunner,
		);
	}
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass the full PK map: .of({ tenantId, id }).loadMany().
  2. If only one id is available, look up the composite map first via the entity repository.
  3. Add a type guard on the public helper that wraps loadMany so callers cannot pass a scalar for composite-PK entities.

Example fix

// before
await qb.relation(Membership,'roles').of(membershipId).loadMany(); // composite PK
// after
await qb.relation(Membership,'roles').of({ tenantId, membershipId }).loadMany();
Defensive patterns

Strategy: type-guard

Validate before calling

import { DataSource } from 'typeorm';

function requireCompositeOf(ds: DataSource, target: Function, of: unknown) {
  const meta = ds.getMetadata(target);
  if (meta.hasMultiplePrimaryKeys && (typeof of !== 'object' || of === null))
    throw new Error(`Entity ${meta.targetName} has composite PK; pass .of({${meta.primaryColumns.map(c => c.propertyName).join(',')}})`);
}

Type guard

function isPkMap(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null;
}

Prevention

When it happens

Trigger: qb.relation(...).of(42).loadMany() (or loadOne) on an entity whose @PrimaryColumn set has more than one column; passing a bare id from a route param for a composite-PK entity.

Common situations: Entity migrated to composite PKs (e.g. adding tenantId to the PK) without updating callers that pass a single id; junction-table entities that are composite by design.

Related errors


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