doctrine/orm · error · LogicException

{argAlias} does not exist

Error message

{argAlias} does not exist

What it means

LogicException from AbstractHydrator row processing (src/Internal/Hydration/AbstractHydrator.php:372) during hydration of `NEW` expressions in DQL (SELECT new DTO(...)). For nested new-object arguments, the hydrator looks up each argument's DQL alias either among collected new objects or row data; when an alias appears in neither, this 'X does not exist' is thrown — the SELECT list references an alias that produced no data in the row.

Source

Thrown at src/Internal/Hydration/AbstractHydrator.php:372

            }
        }

        $nestedEntities = [];
        /**@var string $argAlias */
        foreach ($this->resultSetMapping()->nestedNewObjectArguments as ['ownerIndex' => $ownerIndex, 'argIndex' => $argIndex, 'argAlias' => $argAlias]) {
            if (array_key_exists($argAlias, $rowData['newObjects'])) {
                ksort($rowData['newObjects'][$argAlias]['args']);
                $rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $rowData['newObjects'][$argAlias]['class']->newInstanceArgs($rowData['newObjects'][$argAlias]['args']);
                unset($rowData['newObjects'][$argAlias]);
            } elseif (array_key_exists($argAlias, $rowData['data'])) {
                if (! array_key_exists($argAlias, $nestedEntities)) {
                    $nestedEntities[$argAlias]  = '';
                    $rowData['data'][$argAlias] = $this->hydrateNestedEntity($rowData['data'][$argAlias], $argAlias);
                }

                $rowData['newObjects'][$ownerIndex]['args'][$argIndex] = $rowData['data'][$argAlias];
            } else {
                throw new LogicException($argAlias . ' does not exist');
            }
        }

        foreach (array_keys($nestedEntities) as $entity) {
            unset($rowData['data'][$entity]);
        }

        foreach ($rowData['newObjects'] as $objIndex => $newObject) {
            ksort($rowData['newObjects'][$objIndex]['args']);
            $obj = $rowData['newObjects'][$objIndex]['class']->newInstanceArgs($rowData['newObjects'][$objIndex]['args']);

            $rowData['newObjects'][$objIndex]['obj'] = $obj;
        }

        return $rowData;
    }

    /** @param mixed[] $data pre-hydrated SQL Result Row. */

View on GitHub (pinned to d9b9ff7301)

Solutions

  1. Make sure every alias used as a NEW-expression argument is itself part of the SELECT list (SELECT new DTO(a.name), a FROM ... JOIN ...).
  2. Flatten nested NEW expressions: select the inner new object alias explicitly, or build the nested object in PHP after hydrating the outer DTO.
  3. Remove HINT_FORCE_PARTIAL_LOAD / partial-object hints on that query and retest — partial hydration can drop the data key the alias needs.
  4. If the DQL is minimal and valid, reproduce on the latest patch release and report to doctrine/orm ( hydration LogicException 'alias does not exist').

Example fix

/* before: alias a is only used as a nested NEW argument, never selected */
SELECT new UserDTO(new NameDTO(u.firstName, u.lastName), a)
FROM App\Entity\User u JOIN u.address a

/* after: select the joined alias so the row contains its data */
SELECT new UserDTO(new NameDTO(u.firstName, u.lastName), a)
FROM App\Entity\User u JOIN u.address a -- keep, but hydrate a explicitly if still missing:
-- or: SELECT u, a FROM ... then map to DTO in PHP
Defensive patterns

Strategy: try-catch

Validate before calling

// Before running, sanity-check that every alias used in NEW(...) args appears in the SELECT list
$select = 'SELECT new DTO(new Inner(u.id), a) FROM App\\Entity\\User u JOIN u.address a';
// ensure 'a' is selected/mapped; simplest static guard:
foreach (['u', 'a'] as $alias) {
    if (! preg_match('/\b' . $alias . '\b/', $select)) {
        throw new LogicException("Alias {$alias} used in NEW() but absent from SELECT");
    }
}

Try / catch

try {
    $dtos = $query->getResult();
} catch (\LogicException $e) {
    // 'X does not exist': NEW-expression alias produced no row data.
    // Fall back to hydrating entities/scalars and constructing DTOs in PHP.
    $rows = $em->createQuery('SELECT u, a FROM App\\Entity\\User u JOIN u.address a')->getResult();
    $dtos = array_map(static fn ([$u, $a]) => new UserDTO(NameDTO::from($u), $a), $rows);
}

Prevention

When it happens

Trigger: DQL using NEW-object syntax where a nested argument alias is not (effectively) selected or mapped: e.g. SELECT new DTO(new Inner(u.id), a) ... where alias 'a' was joined but not selected, was pruned by the SQL walker, or the argument is a bare alias the parser recorded in nestedNewObjectArguments while the row contains no such key. Typical shapes: nested NEW expressions over joined entities, NEW combined with partial selects/HINT_FORCE_PARTIAL_LOAD, or scalar aliases reused across NEW arguments.

Common situations: DTO refactoring where someone moved an association into a nested new-expression but forgot to keep the alias in the SELECT; mixing NEW DTO(...) with joins whose aliases are only used inside the NEW argument; edge-case ORM versions where nested new-object arguments over joined aliases are incompletely supported — if the DQL looks legitimate, it may be an ORM bug worth reporting with a reproducer.

Related errors


AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21). Data as JSON: /api/errors/1993b404673d935b. Report an issue: GitHub.