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

  1. Use the two-argument form so the builder knows the entity: `dataSource.createQueryBuilder().relation(User, "photos").of(user).set(photo)`.
  2. Prefer going through the repository, which supplies the entity target automatically: `userRepository.relation("photos").of(user).set(photo)`.
  3. 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

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


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)