BookStackApp/BookStack · error · NotifyException
Only top-level comments can be archived.
Error message
Only top-level comments can be archived.
What it means
CommentRepo::archive() refuses to archive a comment that has a parent_id, i.e. a nested reply. In this library only top-level comments (parent_id null) support the archived state; replies are managed implicitly through their parent. It throws NotifyException with HTTP status 400 so the client sees a user-friendly message.
Source
Thrown at app/Activity/CommentRepo.php:102
public function update(Comment $comment, string $html): Comment
{
$comment->updated_by = user()->id;
$comment->html = HtmlDescriptionFilter::filterFromString($html);
$comment->save();
ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
return $comment;
}
/**
* Archive an existing comment.
*/
public function archive(Comment $comment, bool $log = true): Comment
{
if ($comment->parent_id) {
throw new NotifyException('Only top-level comments can be archived.', '/', 400);
}
$comment->archived = true;
$comment->save();
if ($log) {
ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
}
return $comment;
}
/**
* Un-archive an existing comment.
*/
public function unarchive(Comment $comment, bool $log = true): Comment
{
if ($comment->parent_id) {View on GitHub (pinned to 18f8469a1c)
Solutions
- Check $comment->parent_id is null before calling archive(); skip or handle replies separately.
- If the goal is to remove a reply, delete it (or archive its top-level parent) instead of archiving it directly.
- Catch NotifyException around the call to surface the 400 message to the user.
Example fix
// before
foreach ($comments as $comment) {
$commentRepo->archive($comment);
}
// after
foreach ($comments as $comment) {
if ($comment->parent_id === null) {
$commentRepo->archive($comment);
}
} Defensive patterns
Strategy: validation
Validate before calling
if ($comment->parent_id !== null) {
throw new InvalidArgumentException('Cannot archive a reply; only top-level comments can be archived.');
}
$commentRepo->archive($comment); Type guard
function isTopLevelComment($comment): bool {
return $comment instanceof Comment && $comment->parent_id === null;
} Try / catch
try {
$commentRepo->archive($comment);
} catch (NotifyException $e) {
// handle 400: only top-level comments archivable
} Prevention
- Filter collections to parent_id IS NULL before bulk archive operations.
- Centralize archiving in a helper that asserts top-level status.
- Treat replies as children: archive the parent only.
When it happens
Trigger: Calling CommentRepo->archive($comment) (or the API/controller endpoint that reaches it) with a Comment model whose parent_id is set — i.e. a reply to another comment.
Common situations: Bulk-archiving scripts or moderation tools that iterate over a comment list without filtering out replies; passing a comment id obtained from a nested/reply endpoint; assuming all comments are archivable after a version that introduced archiving for top-level comments only.
Related errors
- Only top-level comments can be un-archived.
- Missing required configuration "{$prop}" value
- Issuer value must start with https://
- auth.registration_email_domain_invalid
- Unauthorized
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/2bcceacd170c8628.
Report an issue: GitHub.