BookStackApp/BookStack · error · PermissionsException

This page is already published or does not belong to you.

Error message

This page is already published or does not belong to you.

What it means

PageController::ensureDraftAccess throws PermissionsException('This page is already published or does not belong to you.') when a draft page operation targets a page that either is no longer a draft ($draft->draft is false) or was created by a different user ($draft->created_by !== user()->id). Draft pages in BookStack are private to their creator until published, so other users (or actions on a since-published page) may not edit/delete them via draft endpoints.

Source

Thrown at app/Entities/Controllers/PageController.php:487

            return redirect($page->getUrl('/copy'));
        }

        $this->checkOwnablePermission(Permission::PageCreate, $newParent);

        $newName = $request->input('name') ?: $page->name;
        $pageCopy = $cloner->clonePage($page, $newParent, $newName);
        $this->showSuccessNotification(trans('entities.pages_copy_success'));

        return redirect($pageCopy->getUrl());
    }

    /**
     * @throws PermissionsException
     */
    protected function ensureDraftAccess(Page $draft): void
    {
        if (!$draft->draft || $draft->created_by !== user()->id) {
            throw new PermissionsException('This page is already published or does not belong to you.');
        }
    }
}

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Check the page is still a draft before calling draft endpoints (page 'draft' flag true) and use the normal page-update endpoint once published
  2. Only the draft creator should perform draft edits/deletes; if collaboration is needed, publish the page and use standard page permissions
  3. Refresh the draft id from the API after publishing, since draft endpoints no longer apply
  4. Handle the PermissionsException in clients and re-fetch the page to get its current state

Example fix

// before
$pages->updateDraft($pageId, $data); // page may already be published
// after
$page = $pages->details($pageId);
if ($page['draft']) { $pages->updateDraft($pageId, $data); }
else { $pages->update($pageId, $data); }
Defensive patterns

Strategy: type-guard

Validate before calling

// Fetch the page first and verify it is still your draft
$page = $api->get("/api/pages/{$id}")->json();
if ($page['draft'] === false || $page['created_by'] !== $myUserId) {
    throw new RuntimeException('Page is published or owned by someone else; use normal page endpoints');
}

Type guard

function isEditableDraft(array $page, int $myUserId): bool {
    return !empty($page['draft']) && (int) $page['created_by'] === $myUserId;
}

Try / catch

try {
    $api->put("/api/pages/{$id}/draft", $data);
} catch (ClientException $e) {
    if ($e->getResponse()->getStatusCode() === 403 || $e->getResponse()->getStatusCode() === 404) {
        // re-fetch page: if published, switch to standard update endpoint
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling editDraft/saveDraft/destroyDraft/store(revision) on a page that was already published (draft flag cleared); one user attempting to modify another user's draft by id; a stale client holding a draft id that has since been published; two users racing to edit the same draft.

Common situations: Scripts caching draft ids across long runs; collaborators expecting shared drafts (BookStack drafts are single-owner); a draft published in another tab while an edit form is still open.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/0b4bcc93e8579ee3. Report an issue: GitHub.