n8n-io/n8n · error · CannotDetermineEntityError
Cannot ${operation}, given value must be instance of entity
Error message
Cannot ${operation}, given value must be instance of entity class, instead object literal is given. Or you must specify an entity target to method call. What it means
`EntityPersistExecutor` resolves the entity target as `this.target ?? entity.constructor`. When you call `manager.save(plainObj)` (no bound target) and `plainObj` is a `{}`-literal, `entity.constructor` is `Object` — meaning TypeORM cannot tell which entity metadata to use. It throws `CannotDetermineEntityError` rather than guessing. Typed repositories have a target so they never hit this.
Source
Thrown at packages/@n8n/typeorm/src/persistence/EntityPersistExecutor.ts:76
}
try {
// collect all operate subjects
const entities: ObjectLiteral[] = Array.isArray(this.entity) ? this.entity : [this.entity];
const entitiesInChunks =
this.options && this.options.chunk && this.options.chunk > 0
? OrmUtils.chunk(entities, this.options.chunk)
: [entities];
// console.time("building subject executors...");
const executors = await Promise.all(
entitiesInChunks.map(async (entities) => {
const subjects: Subject[] = [];
// create subjects for all entities we received for the persistence
entities.forEach((entity) => {
const entityTarget = this.target ? this.target : entity.constructor;
if (entityTarget === Object) throw new CannotDetermineEntityError(this.mode);
let metadata = this.connection
.getMetadata(entityTarget)
.findInheritanceMetadata(entity);
subjects.push(
new Subject({
metadata,
entity: entity,
canBeInserted: this.mode === 'save',
canBeUpdated: this.mode === 'save',
mustBeRemoved: this.mode === 'remove',
canBeSoftRemoved: this.mode === 'soft-remove',
canBeRecovered: this.mode === 'recover',
}),
);
});
View on GitHub (pinned to 5ac6606e81)
Solutions
- Pass the entity class as the target argument: `await manager.save(User, plainObject)`.
- Instantiate the entity first: `manager.save(Object.assign(new User(), dto))`.
- Use the typed `repository.save(...)` from `dataSource.getRepository(User)`, which already carries the target.
Example fix
// before — plain object, no target → error
await dataSource.manager.save({ name: 'Alice', email: 'a@x.com' });
// after — explicit target
await dataSource.manager.save(User, { name: 'Alice', email: 'a@x.com' });
// or
const user = Object.assign(new User(), { name: 'Alice', email: 'a@x.com' });
await dataSource.manager.save(user); Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure plain objects are paired with an entity target before persisting
function isEntityInstance<T>(o: T, ctor: new () => T): o is T {
return o instanceof ctor;
}
async function safeSave<T>(manager: EntityManager, ctor: new () => T, input: Partial<T>) {
const entity = Object.assign(new ctor(), input);
return manager.save(entity); // safe: target is ctor
} Type guard
function isPlainLiteral(o: unknown): o is Record<string, unknown> {
return o !== null && typeof o === 'object' && (o as any).constructor === Object;
} Try / catch
try { await manager.save(payload); } catch (e) { if (e instanceof CannotDetermineEntityError) { /* re-dispatch as manager.save(EntityClass, payload) */ } throw e; } Prevention
- Use typed repositories (`getRepository(Entity)`) instead of the bare EntityManager.
- Instantiate entities (`new Entity()`) or hydrate via `Object.assign(new Entity(), dto)` before saving.
- When using `manager.save`, always pass the entity class as the first argument.
When it happens
Trigger: Calling `dataSource.manager.save({ name: 'x' })` or `manager.remove({ id: 1 })` with a plain object literal and no first-argument entity target. Passing deserialized JSON directly (e.g. `req.body`) into `manager.save`.
Common situations: Using the bare EntityManager instead of a repository. Forgetting to pass the entity class as the first argument: `manager.save(EntityClass, dto)`.
Related errors
- Entity manager is not using single database connection and c
- Removed entity "${subject.metadata.name}" is also scheduled
- Internal error. Subject ${subject.metadata.targetName} must
- Cyclic dependency: ${JSON.stringify(node)}
- Found unknown node. Make sure to provided all involved nodes
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/27acccb0c0fc0f20.
Report an issue: GitHub.