n8n-io/n8n · error · SubjectWithoutIdentifierError

Internal error. Subject ${subject.metadata.targetName} must

Error message

Internal error. Subject ${subject.metadata.targetName} must have an identifier to perform operation.

What it means

Inside `executeUpdateOperations`, each update subject must carry an identifier (the primary-key map) so TypeORM can build `WHERE id = ...`. If `subject.identifier` is falsy it throws `SubjectWithoutIdentifierError`. The error's own docstring says this should never happen in normal use and is most likely an ORM-internal problem, but it surfaces when an update is attempted on an instance whose PK was never set.

Source

Thrown at packages/@n8n/typeorm/src/persistence/SubjectExecutor.ts:431

						if (value !== undefined && value !== null) {
							const preparedValue = this.queryRunner.connection.driver.prepareHydratedValue(
								value,
								column,
							);
							column.setEntityValue(subject.generatedMap!, preparedValue);
						}
					});
				}
			});
		}
	}

	/**
	 * Updates all given subjects in the database.
	 */
	protected async executeUpdateOperations(): Promise<void> {
		const updateSubject = async (subject: Subject) => {
			if (!subject.identifier) throw new SubjectWithoutIdentifierError(subject);

			const updateMap: ObjectLiteral = subject.createValueSetAndPopChangeMap();

			// for tree tables we execute additional queries
			switch (subject.metadata.treeType) {
				case 'nested-set':
					await new NestedSetSubjectExecutor(this.queryRunner).update(subject);
					break;

				case 'closure-table':
					await new ClosureSubjectExecutor(this.queryRunner).update(subject);
					break;

				case 'materialized-path':
					await new MaterializedPathSubjectExecutor(this.queryRunner).update(subject);
					break;
			}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Ensure the entity instance has a non-null primary key before calling update/save (log `metadata.primaryColumns` values).
  2. Confirm the entity class declares a `@PrimaryGeneratedColumn()` / `@PrimaryColumn()`.
  3. If reached via cascade, reproduce with logging and report upstream — per the docs this path is considered an internal bug.

Example fix

// before — id cleared, then update
const u = await repo.findOneBy({ id: 1 });
u.id = undefined;
await repo.save(u); // throws inside executeUpdateOperations

// after — keep the identifier
const u = await repo.findOneBy({ id: 1 });
u.name = 'new';
await repo.save(u);
Defensive patterns

Strategy: validation

Validate before calling

// Before update/save, confirm the instance carries a non-null primary key
function hasId<T>(entity: T, meta: EntityMetadata): boolean {
  return meta.primaryColumns.every(c => c.getEntityValue(entity) != null);
}
if (!hasId(entity, dataSource.getMetadata(EntityClass))) {
  throw new Error('Cannot update: entity has no identifier');
}

Type guard

function hasPrimaryKey<T>(e: T, pk: keyof T): e is T & Record<typeof pk, NonNullable<T[typeof pk]>> {
  return e != null && (e as any)[pk] != null;
}

Try / catch

try { await repo.save(entity); } catch (e) { if (e instanceof SubjectWithoutIdentifierError) { /* log PK columns; reload entity before update */ } throw e; }

Prevention

When it happens

Trigger: Calling `repository.update(entityInstance, ...)` or having cascade-update reach an entity instance whose primary key value is `undefined`/`null`. An entity with no `@PrimaryColumn`/`@PrimaryGeneratedColumn` being updated. Manually clearing the PK on a tracked entity before flush.

Common situations: Loading an entity, setting its id to undefined, then saving. A subclass/SINGLE_TABLE entity whose PK column isn't mapped. A bug in a custom subscriber that nulls the identifier.

Related errors


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