doctrine/orm · error · RuntimeException

Cannot call recomputeSingleEntityChangeSet before computeCha

Error message

Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.

What it means

recomputeSingleEntityChangeSet() diffs an entity's current property values against the 'original data' snapshot the UnitOfWork stored when the entity became managed (during load or computeChangeSets at flush). If originalEntityData for the object id was never set, there is no baseline to diff against, so Doctrine throws instead of producing a wrong change set.

Source

Thrown at src/UnitOfWork.php:1008

        if (! $class->isInheritanceTypeNone()) {
            $class = $this->em->getClassMetadata($entity::class);
        }

        $actualData = [];

        foreach ($class->propertyAccessors as $name => $refProp) {
            if (
                ( ! $class->isIdentifier($name) || ! $class->isIdGeneratorIdentity())
                && ($name !== $class->versionField)
                && ! $class->isCollectionValuedAssociation($name)
            ) {
                $actualData[$name] = $refProp->getValue($entity);
            }
        }

        if (! isset($this->originalEntityData[$oid])) {
            throw new RuntimeException('Cannot call recomputeSingleEntityChangeSet before computeChangeSet on an entity.');
        }

        $originalData = $this->originalEntityData[$oid];
        $changeSet    = [];

        foreach ($actualData as $propName => $actualValue) {
            $orgValue = $originalData[$propName] ?? null;

            if (isset($class->fieldMappings[$propName]->enumType)) {
                if (is_array($orgValue)) {
                    foreach ($orgValue as $id => $val) {
                        if ($val instanceof BackedEnum) {
                            $orgValue[$id] = $val->value;
                        }
                    }
                } else {
                    if ($orgValue instanceof BackedEnum) {
                        $orgValue = $orgValue->value;

View on GitHub (pinned to d9b9ff7301)

Solutions

  1. Guard the call: only recompute when $uow->getEntityState($entity) === UnitOfWork::STATE_MANAGED.
  2. For newly created entities, persist them and let the normal flush compute their insert; do not recompute change sets for them.
  3. If the entity is detached or from another EM, reload it through this EntityManager ($em->find(...)), mutate, then recompute.

Example fix

// before
$uow = $em->getUnitOfWork();
$uow->recomputeSingleEntityChangeSet($classMeta, $entity); // throws for unmanaged/new entities

// after
$uow = $em->getUnitOfWork();
if ($uow->getEntityState($entity) === UnitOfWork::STATE_MANAGED) {
    $uow->recomputeSingleEntityChangeSet($classMeta, $entity);
}
Defensive patterns

Strategy: type-guard

Type guard

// Narrow to entities the UnitOfWork can diff (managed, with an original-data snapshot)
function isRecomputable(EntityManager $em, object $entity): bool
{
    return $em->getUnitOfWork()->getEntityState($entity)
        === UnitOfWork::STATE_MANAGED;
}

Try / catch

// In listeners: recompute only when managed, otherwise let normal persist/flush handle it
$uow = $em->getUnitOfWork();
if ($uow->getEntityState($entity) === UnitOfWork::STATE_MANAGED) {
    $uow->recomputeSingleEntityChangeSet($em->getClassMetadata($entity::class), $entity);
}

Prevention

When it happens

Trigger: Calling $em->getUnitOfWork()->recomputeSingleEntityChangeSet($classMetadata, $entity) on an entity that was never managed by this EntityManager in a computed state: a NEW entity built in code (persisted but not yet flushed/computed), a DETACHED entity, an entity belonging to a different EntityManager, or any entity after $em->clear()/detach().

Common situations: onFlush / lifecycle-listener helper code that mutates entities and recomputes change sets; helper functions called for both freshly constructed and managed entities; listener code surviving an $em->clear() in long-running workers.

Related errors


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