getgrav/grav · warning · RuntimeException

Failed: Cannot set page parent to a child of current page

Error message

Failed: Cannot set page parent to a child of current page

What it means

The second guard in FlexPageObject::move() rejects moving a page under its own descendant: Utils::startsWith($parent->rawRoute(), $this->rawRoute()) detects that the target parent's route is prefixed by the current page's route, which would create a cycle in the page tree. The operation is aborted before any state changes (storeOriginal() runs only after both checks).

Source

Thrown at system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php:279

        throw new RuntimeException(__METHOD__ . '(): Not Implemented');
    }

    /**
     * Prepare move page to new location. Moves also everything that's under the current page.
     *
     * You need to call $this->save() in order to perform the move.
     *
     * @param PageInterface $parent New parent page.
     * @return $this
     */
    public function move(PageInterface $parent)
    {
        if ($this->route() === $parent->route()) {
            throw new RuntimeException('Failed: Cannot set page parent to self');
        }
        $rawRoute = $this->rawRoute();
        if ($rawRoute && Utils::startsWith($parent->rawRoute(), $rawRoute)) {
            throw new RuntimeException('Failed: Cannot set page parent to a child of current page');
        }

        $this->storeOriginal();

        // TODO:
        throw new RuntimeException(__METHOD__ . '(): Not Implemented');
    }

    /**
     * Prepare a copy from the page. Copies also everything that's under the current page.
     *
     * Returns a new Page object for the copy.
     * You need to call $this->save() in order to perform the move.
     *
     * @param PageInterface|null $parent New parent page.
     * @return $this
     */
    public function copy(?PageInterface $parent = null)

View on GitHub (pinned to 6040efed04)

Solutions

  1. Verify direction before moving: reject when str_starts_with($parent->rawRoute(), $page->rawRoute()) in your own UI logic.
  2. When moving a whole branch, pass the new parent from outside the branch only.
  3. Fix argument order if the intended operation was moving the child under the page.

Example fix

// before
$ancestor->move($descendant); // inverted / cyclic

// after
if (!str_starts_with($descendant->rawRoute(), $ancestor->rawRoute())) {
    $ancestor->move($descendant);
}
Defensive patterns

Strategy: validation

Validate before calling

// Block moving a page into its own subtree (descendant check)
$rawRoute = $page->rawRoute();
if ($rawRoute && !str_starts_with($parent->rawRoute(), $rawRoute)) {
    $page->move($parent);
    $page->save();
}

Try / catch

try {
    $page->move($parent);
} catch (\Grav\Framework\Flex\Exception\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'child of current page')) {
        $ui->setError('Cannot move a page under one of its own children.');
        return;
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling $parentPage->move($childPage) with inverted arguments; picking a grandchild as the new parent in a move UI; programmatic re-parenting computed from a flattened route list where a descendant is selected.

Common situations: Admin tree drag-and-drop implemented via move() that allows dropping a branch into itself; automated restructuring scripts that walk the tree while mutating it.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/0b6189ab144fb2f3. Report an issue: GitHub.