getgrav/grav · warning · RuntimeException

Failed: Cannot set page parent to self

Error message

Failed: Cannot set page parent to self

What it means

FlexPageObject::move() validates the target parent before doing anything; if the new parent's route equals the page's own route (i.e. you are setting the page as its own parent), it throws 'Cannot set page parent to self'. The check compares routes, so aliases or duplicates resolving to the same route also trigger it. It is a self-reference guard, not a storage failure.

Source

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

     */
    public function file(): ?MarkdownFile
    {
        // TODO:
        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.
     *

View on GitHub (pinned to 6040efed04)

Solutions

  1. Guard the call: if ($parent->route() !== $page->route()) { $page->move($parent); }.
  2. In move forms, exclude the page itself (and its descendants) from the selectable parents.
  3. If the goal was only reordering among siblings, use the ordering/metadata API instead of move().

Example fix

// before
$page->move($selectedParent);

// after
if ($selectedParent->route() !== $page->route()) {
    $page->move($selectedParent);
}
Defensive patterns

Strategy: validation

Validate before calling

// Reject a no-op self-move before calling the API
if ($parent->route() !== $page->route()) {
    $page->move($parent);
    $page->save();
}

Try / catch

try {
    $page->move($parent);
} catch (\Grav\Framework\Flex\Exception\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'Cannot set page parent to self')) {
        // treat as no-op: user re-selected the current parent
        return; // or show a notice
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling $page->move($page); passing a parent object that is actually the same page fetched again (e.g. via a route lookup that resolves back to it); forms that let users pick a parent and default the selection to the current page.

Common situations: Admin page-move UIs where the current page appears in the parent dropdown; programmatic tree restructuring with index math that can select the node itself.

Related errors


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