BookStackApp/BookStack · error · MoveOperationException
Book or chapter to move page into not found
Error message
Book or chapter to move page into not found
What it means
PageRepo::move resolves the destination via findVisibleByStringIdentifier; pages may be moved into a Chapter or a Book. If the resolved entity is neither (missing, invisible, a Shelf, or another Page), it throws MoveOperationException('Book or chapter to move page into not found').
Source
Thrown at app/Entities/Repos/PageRepo.php:279
$this->baseRepo->sortParent($page);
return $page;
}
/**
* Move the given page into a new parent book or chapter.
* The $parentIdentifier must be a string of the following format:
* 'book:<id>' (book:5).
*
* @throws MoveOperationException
* @throws PermissionsException
*/
public function move(Page $page, string $parentIdentifier): Entity
{
$parent = $this->entityQueries->findVisibleByStringIdentifier($parentIdentifier);
if (!$parent instanceof Chapter && !$parent instanceof Book) {
throw new MoveOperationException('Book or chapter to move page into not found');
}
if (!userCan(Permission::PageCreate, $parent)) {
throw new PermissionsException('User does not have permission to create a page within the new parent');
}
return (new DatabaseTransaction(function () use ($page, $parent) {
$page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null;
$newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id;
$this->parentChanger->changeBook($page, $newBookId);
$page->rebuildPermissions();
Activity::add(ActivityType::PAGE_MOVE, $page);
$this->baseRepo->sortParent($page);
return $parent;
}))->run();View on GitHub (pinned to 18f8469a1c)
Solutions
- Confirm the identifier refers to an existing, visible Book or Chapter (fetch it first and check the type).
- Don't pass Shelf or Page identifiers — resolve the chapter/book the page should live in.
- Check the target isn't in the trash and the user has view permission on it.
- Catch MoveOperationException around move() and prompt the user to pick a valid parent.
Example fix
// before
$pageRepo->move($page, $shelfId); // shelves are not valid page parents
// after
$parent = $entityQueries->findVisibleByStringIdentifier($parentId);
if ($parent instanceof Book || $parent instanceof Chapter) {
$pageRepo->move($page, $parentId);
} Defensive patterns
Strategy: validation
Validate before calling
$parent = $entityQueries->findVisibleByStringIdentifier($identifier);
if (!($parent instanceof \BookStack\Entities\Book || $parent instanceof \BookStack\Entities\Chapter)) {
throw new \InvalidArgumentException('Parent must be a Book or Chapter');
} Type guard
function isValidPageParent(?object $e): bool {
return $e instanceof \BookStack\Entities\Book || $e instanceof \BookStack\Entities\Chapter;
} Try / catch
try {
$pageRepo->move($page, $identifier);
} catch (\BookStack\Exceptions\MoveOperationException $e) {
return back()->withErrors(['parent' => $e->getMessage()]);
} Prevention
- Never use Shelf or Page identifiers as page parents
- Verify the parent exists and is visible to the acting user
- Check trash/deletion status of the parent before bulk moves
When it happens
Trigger: move($page, $parentIdentifier) with a nonexistent identifier, a Shelf or Page ID, a soft-deleted parent, or an identifier for an entity the user cannot view (lookup returns null).
Common situations: Bulk-migration scripts referencing IDs from another environment; passing a shelf ID assuming pages can live on shelves; target chapter deleted before the move; permission restrictions hiding the target so it resolves to null.
Related errors
- Book to move chapter into not found
- Selected parent is not a book or chapter
- auth.registration_email_domain_invalid
- errors.chapter_not_found
- errors.page_not_found
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/60527c2871af8b32.
Report an issue: GitHub.