cakephp/cakephp · error · DatabaseException

Cannot set a node's parent as itself.

Error message

Cannot set a node's parent as itself.

What it means

TreeBehavior::beforeSave() rejects a save where the entity's parent_id points at the entity itself, which would create a self-referencing cycle and corrupt the MPTT tree (left/right bounds). The save is aborted with a DatabaseException instead of persisting the invalid hierarchy.

Solutions

  1. Do not set the entity's parent field to its own primary key; omit the parent field or set it to a different node's id.
  2. Validate the parent id against the entity's primary key before assigning it and before saving.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/ORM/Behavior/TreeBehavior.php:114 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of cakephp/cakephp@1128eba9b0 (2026-09-12). Data as JSON: /api/errors/78c428eecb27cfbe. Report an issue: GitHub.

Appendix: source

Thrown at src/ORM/Behavior/TreeBehavior.php:114

     * Transparently manages setting the lft and rght fields if the parent field is
     * included in the parameters to be saved.
     *
     * @param \Cake\Event\EventInterface<\Cake\ORM\Table> $event The beforeSave event that was fired
     * @param \Cake\Datasource\EntityInterface $entity the entity that is going to be saved
     * @return void
     * @throws \Cake\Database\Exception\DatabaseException if the parent to set for the node is invalid
     */
    public function beforeSave(EventInterface $event, EntityInterface $entity): void
    {
        $isNew = $entity->isNew();
        $config = $this->getConfig();
        $parent = $entity->get($config['parent']);
        $primaryKey = $this->_getPrimaryKey();
        $dirty = $entity->isDirty($config['parent']);
        $level = $config['level'];

        if ($parent && $entity->get($primaryKey) === $parent) {
            throw new DatabaseException("Cannot set a node's parent as itself.");
        }

        if ($isNew) {
            if ($parent) {
                $parentNode = $this->_getNode($parent);
                $edge = $parentNode->get($config['right']);
                $entity->set($config['left'], $edge);
                $entity->set($config['right'], $edge + 1);
                $this->_sync(2, '+', ">= {$edge}");

                if ($level) {
                    $entity->set($level, $parentNode[$level] + 1);
                }

                return;
            }

            $edge = $this->_getMax();

View on GitHub (pinned to 1128eba9b0)