toeverything/AFFiNE · error · CommentNotFound

comment_not_found

comment_not_found

Error message

Comment not found.

What it means

CommentNotFound (resource_not_found / comment_not_found) thrown by CommentModel.createReply (packages/backend/server/src/models/comment.ts:285) when replying to a comment whose id does not resolve via this.get(data.commentId). The ReplyCreate schema parsed successfully, but the parent comment row is missing or not visible to the caller. Surfaced as a structured UserFriendlyError, so GraphQL/REST clients see a clean not_found.

Source

Thrown at packages/backend/server/src/models/comment.ts:285

    return changes;
  }

  // #endregion

  // #region Reply

  /**
   * Reply to a comment
   * @param input - The reply create input
   * @returns The created reply
   */
  async createReply(input: ReplyCreate) {
    const data = ReplyCreateSchema.parse(input);
    // find comment
    const comment = await this.get(data.commentId);
    if (!comment) {
      throw new CommentNotFound();
    }

    return (await this.db.reply.create({
      data: {
        ...data,
        workspaceId: comment.workspaceId,
        docId: comment.docId,
      },
    })) as Reply;
  }

  async getReply(id: string) {
    return (await this.db.reply.findUnique({
      where: { id, deletedAt: null },
    })) as Reply | null;
  }

  async listReplies(workspaceId: string, docId: string, commentId: string) {

View on GitHub (pinned to 26c515e050)

Solutions

  1. Verify the comment exists and is in scope before submitting the reply: GET /workspaces/{ws}/docs/{doc}/comments/{commentId}.
  2. If the comment was deleted, discard the reply draft or recreate the parent comment first.
  3. Catch CommentNotFound by code and surface a user-facing 'This comment no longer exists' message in the UI.
  4. In tests, ensure the parent comment is created in the same workspace before createReply.

Example fix

// before
await commentModel.createReply({ commentId, body });
// after
const parent = await commentModel.get(commentId);
if (!parent) throw new Error('Cannot reply: parent comment is gone.');
await commentModel.createReply({ commentId, body });
Defensive patterns

Strategy: validation

Validate before calling

const parent = await commentModel.get(commentId);
if (!parent) {
  throw new Error(`Cannot reply: parent comment ${commentId} not found`);
}
await commentModel.createReply({ commentId, body });

Type guard

const commentExists = (c: Comment | null): c is Comment => c !== null;

Try / catch

try {
  await commentModel.createReply(input);
} catch (e) {
  if (e instanceof UserFriendlyError && e.code === 'comment_not_found') {
    res.status(404).send('This comment no longer exists.');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling createReply with a ReplyCreate payload whose commentId refers to a deleted, non-existent, or workspace-inaccessible comment. this.get() returns null (comment filtered by workspace/scope or simply absent), so the guard throws before inserting into the reply table.

Common situations: Client holds a stale commentId after the comment was deleted; reply posted to a comment in a different workspace; race where the comment is removed between UI load and reply submit; test fixture that forgot to seed the parent comment.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/a667b838ec7623e2. Report an issue: GitHub.