toeverything/AFFiNE · error · ReplyNotFound
reply_not_found
reply_not_found
Error message
Reply not found.
What it means
GraphQL mutation updateReply (resolver.ts:202) throws ReplyNotFound when service.getReply(input.id) returns null. The reply (comment sub-item) does not exist; the check runs before 'Doc.Comments.Update' permission and before service.updateReply.
Solutions
- Refetch the thread and only edit replies still present.
- Treat a vanished parent comment as removing all its replies; drop local reply state with it.
- Keep the reply editor bound to a live subscription so deleted replies disable editing immediately.
Defensive patterns
Strategy: try-catch
Validate before calling
const replyAlive = (await comments(docId)).some(c => c.replies?.some(r => r.id === input.id));
if (!replyAlive) { /* disable edit action */ } Type guard
function isReplyNotFound(e: unknown): boolean {
return (
e instanceof GraphQLError &&
(e.extensions?.code === 'reply_not_found' ||
e.message === 'Reply not found.')
);
} Try / catch
try {
await updateReply({ id, content });
} catch (e) {
if (isReplyNotFound(e)) {
// reply (or its parent) is gone: refetch thread, drop stale editor
} else throw e;
} Prevention
- Deleting a parent comment removes its replies - invalidate reply state with the parent.
- Bind reply editors to the live subscription snapshot for that doc.
- Do not retry an update against a reply id that already 404'd.
When it happens
Trigger: Calling updateReply with a reply id that is deleted or unknown - e.g. the parent comment (and its replies) was deleted while the reply editor was open.
Common situations: Editing a reply in a thread that a collaborator deleted (deleting a comment removes its replies); stale reply id from an outdated subscription snapshot.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- comment_not_found
- access_denied
- action_forbidden
- action_forbidden
- action_forbidden_on_non_team_workspace
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/88c778163e995138.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/comment/resolver.ts:202
...reply,
user: {
id: me.id,
name: me.name,
avatarUrl: me.avatarUrl,
},
};
}
@Mutation(() => Boolean, {
description: 'Update a reply content',
})
async updateReply(
@CurrentUser() me: UserType,
@Args('input') input: ReplyUpdateInput
) {
const reply = await this.service.getReply(input.id);
if (!reply) {
throw new ReplyNotFound();
}
await this.assertPermission(me, reply, 'Doc.Comments.Update');
await this.service.updateReply(input);
publishCommentChanged(this.realtime, reply.workspaceId, reply.docId);
return true;
}
@Mutation(() => Boolean, {
description: 'Delete a reply',
})
async deleteReply(@CurrentUser() me: UserType, @Args('id') id: string) {
const reply = await this.service.getReply(id);
if (!reply) {
throw new ReplyNotFound();
}
View on GitHub (pinned to b4c8548c09)