{"record":{"id":"0b6189ab144fb2f3","repo":"getgrav/grav","slug":"failed-cannot-set-page-parent-to-a-child-of-curre-0b6189","errorCode":null,"errorMessage":"Failed: Cannot set page parent to a child of current page","messagePattern":"Failed: Cannot set page parent to a child of current page","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"warning","filePath":"system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php","lineNumber":279,"sourceCode":"        throw new RuntimeException(__METHOD__ . '(): Not Implemented');\n    }\n\n    /**\n     * Prepare move page to new location. Moves also everything that's under the current page.\n     *\n     * You need to call $this->save() in order to perform the move.\n     *\n     * @param PageInterface $parent New parent page.\n     * @return $this\n     */\n    public function move(PageInterface $parent)\n    {\n        if ($this->route() === $parent->route()) {\n            throw new RuntimeException('Failed: Cannot set page parent to self');\n        }\n        $rawRoute = $this->rawRoute();\n        if ($rawRoute && Utils::startsWith($parent->rawRoute(), $rawRoute)) {\n            throw new RuntimeException('Failed: Cannot set page parent to a child of current page');\n        }\n\n        $this->storeOriginal();\n\n        // TODO:\n        throw new RuntimeException(__METHOD__ . '(): Not Implemented');\n    }\n\n    /**\n     * Prepare a copy from the page. Copies also everything that's under the current page.\n     *\n     * Returns a new Page object for the copy.\n     * You need to call $this->save() in order to perform the move.\n     *\n     * @param PageInterface|null $parent New parent page.\n     * @return $this\n     */\n    public function copy(?PageInterface $parent = null)","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php#L261-L297","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify direction before moving: reject when str_starts_with($parent->rawRoute(), $page->rawRoute()) in your own UI logic.","When moving a whole branch, pass the new parent from outside the branch only.","Fix argument order if the intended operation was moving the child under the page."],"exampleFix":"// before\n$ancestor->move($descendant); // inverted / cyclic\n\n// after\nif (!str_starts_with($descendant->rawRoute(), $ancestor->rawRoute())) {\n    $ancestor->move($descendant);\n}","handlingStrategy":"validation","validationCode":"// Block moving a page into its own subtree (descendant check)\n$rawRoute = $page->rawRoute();\nif ($rawRoute && !str_starts_with($parent->rawRoute(), $rawRoute)) {\n    $page->move($parent);\n    $page->save();\n}","typeGuard":null,"tryCatchPattern":"try {\n    $page->move($parent);\n} catch (\\Grav\\Framework\\Flex\\Exception\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'child of current page')) {\n        $ui->setError('Cannot move a page under one of its own children.');\n        return;\n    }\n    throw $e;\n}","preventionTips":["Filter the candidate parent list to routes outside the page's own subtree.","Keep argument order straight: move($newParent) moves THIS page under $newParent.","Never mutate and walk the page tree in the same pass during restructuring."],"tags":["grav","flex-pages","page-tree","move","cycle-detection"],"backgroundTag":"circular-reference","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}