BookStackApp/BookStack · error · NotifyException
Only top-level comments can be un-archived.
Error message
Only top-level comments can be un-archived.
What it means
CommentRepo::unarchive() mirrors archive(): it only operates on top-level comments and throws NotifyException (HTTP 400) if the given Comment has a parent_id. Replies inherit visibility from their parent, so they cannot be un-archived individually.
Source
Thrown at app/Activity/CommentRepo.php:121
}
$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) {
throw new NotifyException('Only top-level comments can be un-archived.', '/', 400);
}
$comment->archived = false;
$comment->save();
if ($log) {
ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
}
return $comment;
}
/**
* Delete a comment from the system.
*/
public function delete(Comment $comment): void
{
$comment->delete();View on GitHub (pinned to 18f8469a1c)
Solutions
- Verify $comment->parent_id is null before calling unarchive().
- Un-archive the top-level parent comment instead of the reply.
- Wrap the call in try-catch for NotifyException and report the 400 message.
Example fix
// before
$comment = Comment::findOrFail($id);
$commentRepo->unarchive($comment);
// after
$comment = Comment::findOrFail($id);
if ($comment->parent_id !== null) {
$comment = $comment->parentComment ?? Comment::findOrFail($comment->parent_id);
}
$commentRepo->unarchive($comment); Defensive patterns
Strategy: validation
Validate before calling
if ($comment->parent_id !== null) {
// resolve to top-level parent instead
$comment = Comment::findOrFail($comment->parent_id);
}
$commentRepo->unarchive($comment); Type guard
function isTopLevelComment($comment): bool {
return $comment instanceof Comment && $comment->parent_id === null;
} Try / catch
try {
$commentRepo->unarchive($comment);
} catch (NotifyException $e) {
// handle 400: only top-level comments can be un-archived
} Prevention
- Always resolve a reply to its parent before restore operations.
- Track which ids in stored state are top-level comments.
- Add integration tests covering reply restore paths.
When it happens
Trigger: Calling CommentRepo->unarchive($comment) (or its endpoint) with a reply — any Comment model where parent_id is not null.
Common situations: Restoring archived content from a backup/export where reply records are processed independently; admin UI or API client that lost track of which ids are top-level; scripts syncing comment state between environments.
Related errors
- Only top-level comments can be 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/98166d4a04ebf17e.
Report an issue: GitHub.