n8n-io/n8n · error · SubjectRemovedAndUpdatedError

Removed entity "${subject.metadata.name}" is also scheduled

Error message

Removed entity "${subject.metadata.name}" is also scheduled for update operation. Make sure you are not updating and removing same object (note that update or remove may be executed by cascade operations).

What it means

Before flushing, `SubjectExecutor.validate()` checks every subject and rejects any subject flagged with both `mustBeUpdated` and `mustBeRemoved`. TypeORM cannot both update and delete the same row, and the conflict usually arises indirectly through cascade rules touching the same instance from two paths.

Source

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

			// console.time(".broadcastAfterEventsForAll");
			broadcasterResult = this.broadcastAfterEventsForAll();
			if (broadcasterResult.promises.length > 0) await Promise.all(broadcasterResult.promises);
			// console.timeEnd(".broadcastAfterEventsForAll");
		}
		// console.timeEnd("SubjectExecutor.execute");
	}

	// -------------------------------------------------------------------------
	// Protected Methods
	// -------------------------------------------------------------------------

	/**
	 * Validates all given subjects.
	 */
	protected validate() {
		this.allSubjects.forEach((subject) => {
			if (subject.mustBeUpdated && subject.mustBeRemoved)
				throw new SubjectRemovedAndUpdatedError(subject);
		});
	}

	/**
	 * Performs entity re-computations - finds changed columns, re-builds insert/update/remove subjects.
	 */
	protected recompute(): void {
		new SubjectChangedColumnsComputer().compute(this.allSubjects);
		this.insertSubjects = this.allSubjects.filter((subject) => subject.mustBeInserted);
		this.updateSubjects = this.allSubjects.filter((subject) => subject.mustBeUpdated);
		this.removeSubjects = this.allSubjects.filter((subject) => subject.mustBeRemoved);
		this.softRemoveSubjects = this.allSubjects.filter((subject) => subject.mustBeSoftRemoved);
		this.recoverSubjects = this.allSubjects.filter((subject) => subject.mustBeRecovered);
		this.hasExecutableOperations =
			this.insertSubjects.length > 0 ||
			this.updateSubjects.length > 0 ||
			this.removeSubjects.length > 0 ||
			this.softRemoveSubjects.length > 0 ||

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Split the unit of work: `await em.save(parentA)` and `await em.remove(parentB)` in separate flushes so the child is not double-scheduled.
  2. Narrow `cascade` on the relation to only the operations you need (e.g. `cascade: ['insert', 'update']` without 'remove').
  3. Detach the shared entity from one relation before persisting so it is not simultaneously updated and removed.

Example fix

// before — one flush updates and removes the same child
await dataSource.manager.save([parentA]);   // schedules child update
await dataSource.manager.remove([parentB]); // schedules child remove (same flush)

// after — separate flushes
await dataSource.manager.save([parentA]);
await dataSource.manager.remove([parentB]);
Defensive patterns

Strategy: validation

Validate before calling

// Before flushing, ensure no entity is referenced by both an update and a remove path
// (simple heuristic for a single manager scope): track ids you save vs remove
const savedIds = new Set<string>();
const removedIds = new Set<string>();
const key = (meta, e) => `${meta.name}#${meta.primaryColumns.map(c => c.getEntityValue(e)).join(':')}`;
// call before remove: if (savedIds.has(key(...))) throw 'subject both updated and removed';

Try / catch

try { await dataSource.manager.save(entities); } catch (e) { if (e instanceof SubjectRemovedAndUpdatedError) { /* split into separate flushes / narrow cascade */ } throw e; }

Prevention

When it happens

Trigger: A relation configured with `cascade: ['update', 'remove']` (or `cascade: true`) where the same entity is reached twice in one persistence pass — once scheduled for update (e.g. its parent was saved) and once for removal (e.g. another relation removed it). Detaching an entity from one collection while simultaneously editing it through another.

Common situations: Two parents share a child entity; saving parent A schedules the child for update while removing parent B schedules it for delete in the same unit of work. A @ManyToMany with cascade remove combined with a separate update of the join target.

Related errors


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