doctrine/orm · error · LengthException
Unexpected empty result for database query.
Error message
Unexpected empty result for database query.
What it means
After an INSERT/UPDATE flush, BasicEntityPersister::assignDefaultVersionAndUpsertableValues() re-SELECTs version and generated (insertable:false/updatable:false) columns for the written row by its identifier (fetchVersionAndNotUpsertableValues). fetchNumeric() returning false means the row the UnitOfWork just wrote cannot be read back, which should be impossible in a consistent transaction, so it throws LengthException to surface the inconsistency.
Source
Thrown at src/Persisters/Entity/BasicEntityPersister.php:331
$tableName = $this->quoteStrategy->getTableName($versionedClass, $this->platform);
$identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform);
// FIXME: Order with composite keys might not be correct
$sql = 'SELECT ' . implode(', ', $columnNames)
. ' FROM ' . $tableName
. ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?';
$flatId = $this->identifierFlattener->flattenIdentifier($versionedClass, $id);
$values = $this->conn->fetchNumeric(
$sql,
array_values($flatId),
$this->extractIdentifierTypes($id, $versionedClass),
);
if ($values === false) {
throw new LengthException('Unexpected empty result for database query.');
}
$values = array_combine(array_keys($columnNames), $values);
if (! $values) {
throw new LengthException('Unexpected number of database columns.');
}
return $values;
}
/**
* @param mixed[] $id
*
* @return list<ParameterType|int|string>
* @phpstan-return list<ParameterType::*|ArrayParameterType::*|string>
*/
final protected function extractIdentifierTypes(array $id, ClassMetadata $versionedClass): arrayView on GitHub (pinned to d9b9ff7301)
Solutions
- Check for database triggers or row-level security on the table that remove/hide the row after the write
- Ensure the flush's write AND re-select run on the same connection and inside the same transaction (no read/write splitting mid-flush)
- Verify identifier mapping (custom DBAL types on the id column can make the WHERE clause not match)
- If another process legitimately deletes during flush, reorder the operations (perform the delete after flush, or detach the entity)
Defensive patterns
Strategy: validation
Validate before calling
// Guard the environment, not the call: assert the row is visible on the same connection before flush-heavy paths
$row = $conn->fetchOne('SELECT 1 FROM ' . $meta->getTableName() . ' WHERE ' . $meta->getSingleIdentifierColumnName() . ' = ?', [$id]);
if ($row === false) { /* do not proceed with generated-column/versioned writes */ } Try / catch
try { $em->flush(); } catch (LengthException $e) { if ($e->getMessage() === 'Unexpected empty result for database query.') { /* inspect triggers / replica routing, do not blind-retry */ } throw $e; } Prevention
- Audit AFTER INSERT/UPDATE triggers on tables with versioned/generated columns
- Keep the flush on one connection and transaction (no replica routing mid-flush)
- Test entities with #[Version] and generated columns against the real DBMS in CI
When it happens
Trigger: flush() on an entity with #[Version] or generated columns where the row is gone at re-select time: an AFTER INSERT/UPDATE trigger deleting or moving the row, a concurrent delete committed between the write and the re-select, row-level security/tenant filters hiding the row, or a read/write-split connection routing the SELECT to a replica with lag.
Common situations: MySQL/Postgres triggers; multi-tenant RLS policies; Doctrine with a connection wrapper that sends reads to a read replica mid-flush; identifiers mangled by a custom DBAL type so the WHERE clause selects nothing.
Related errors
- Unexpected empty result for database query.
- Attempting to change readonly property %s::$%s.
- Unexpected number of database columns.
- Commit failed
- Cannot call recomputeSingleEntityChangeSet before computeCha
AI-assisted analysis of doctrine/orm@d9b9ff7301 (2026-08-21).
Data as JSON: /api/errors/b53f0b83f505ffa8.
Report an issue: GitHub.