typeorm/typeorm · error · TypeORMError
Entity to work with is not specified!
Error message
Entity to work with is not specified!
What it means
Thrown by the `relationMetadata` getter in QueryExpressionMap when no main alias has been set on the query builder. The getter is invoked every time a RelationQueryBuilder operation (set/add/remove/loadMany/loadOne) needs to resolve which entity class the relation lives on. Without a main alias the builder cannot look up relation metadata, so it aborts before issuing any SQL. This is a programming/usage error, not a data or database problem.
Solutions
- Use the two-argument form so the builder knows the entity: `dataSource.createQueryBuilder().relation(User, "photos").of(user).set(photo)`.
- Prefer going through the repository, which supplies the entity target automatically: `userRepository.relation("photos").of(user).set(photo)`.
- If you already have a SelectQueryBuilder with a main alias, chain `.relation()` off that builder instead of creating a fresh one from the DataSource.
Example fix
// before
await dataSource
.createQueryBuilder()
.relation("photos") // no entity target -> main alias unset
.of(user)
.set(photo);
// after (two-argument form)
await dataSource
.createQueryBuilder()
.relation(User, "photos") // entity target -> main alias set
.of(user)
.set(photo);
// after (via repository)
await userRepository
.relation("photos")
.of(user)
.set(photo); Defensive patterns
Strategy: validation
Validate before calling
// Before calling relation ops, ensure a main alias exists.
function assertRelationTarget(qb: SelectQueryBuilder<any>) {
if (!qb.expressionMap.mainAlias) {
throw new Error("Call .relation(Entity, 'prop') with an entity target, or use a Repository.");
}
} Type guard
function hasMainAlias(qb: QueryBuilder<any>): boolean {
return !!qb.expressionMap.mainAlias;
} Prevention
- Prefer Repository.relation(prop) — it injects the entity target automatically.
- When using DataSource.createQueryBuilder(), always pass two arguments to .relation().
- Wrap relation-API usage in a typed helper that asserts the main alias is set.
When it happens
Trigger: Calling `.relation("someProp")` (single-argument form) on a raw DataSource/EntityManager QueryBuilder that has no main alias, then invoking `.of(x).set(y)` / `.add(y)` / `.loadMany()`. The single-argument `relation(propertyPath)` overload deliberately does NOT set a main alias; only the two-argument `relation(EntityTarget, propertyPath)` does. Any subsequent access to `expressionMap.relationMetadata` triggers the throw.
Common situations: Using `dataSource.createQueryBuilder().relation("photos").of(user).set(photo)` instead of going through a typed repository; refactoring code that previously called `.relation()` on a `Repository<T>` (which injects the entity target) to call it on a bare EntityManager; copying a snippet from a tutorial that assumed a repository context into a DataSource-based context.
Related errors
- Relation was not found in entity
- Add operation is only supported for many-to-many and…
- Add operation is only supported for many-to-many and…
- Cannot load entity because only one primary key was…
- Entity whose relation needs to be set is not set. Use .of…
AI-assisted analysis of typeorm/typeorm@df07bf1ef4 (2026-08-07).
Data as JSON: /api/errors/65b67e377487bb26.
Report an issue: GitHub.
Appendix: source
Thrown at src/query-builder/QueryExpressionMap.ts:465
return alias
}
findColumnByAliasExpression(
aliasExpression: string,
): ColumnMetadata | undefined {
const [aliasName, propertyPath] = aliasExpression.split(".")
const alias = this.findAliasByName(aliasName)
return alias.metadata.findColumnWithPropertyName(propertyPath)
}
/**
* Gets relation metadata of the relation this query builder works with.
*
* todo: add proper exceptions
*/
get relationMetadata(): RelationMetadata {
if (!this.mainAlias)
throw new TypeORMError(`Entity to work with is not specified!`) // todo: better message
const relationMetadata =
this.mainAlias.metadata.findRelationWithPropertyPath(
this.relationPropertyPath,
)
if (!relationMetadata)
throw new TypeORMError(
`Relation ${this.relationPropertyPath} was not found in entity ${this.mainAlias.name}`,
) // todo: better message
return relationMetadata
}
/**
* Copies all properties of the current QueryExpressionMap into a new one.
* Useful when QueryBuilder needs to create a copy of itself.
*/
clone(): QueryExpressionMap {View on GitHub (pinned to df07bf1ef4)