{"record":{"id":"3249c206fe82374c","repo":"paperclipai/paperclip","slug":"only-the-queued-message-author-can-edit-it","errorCode":null,"errorMessage":"Only the queued message author can edit it","messagePattern":"Only the queued message author can edit it","errorType":"exception","errorClass":"QueuedCommentMutationForbiddenError","httpStatus":null,"severity":"error","filePath":"server/src/modules/wake-queue/application/queued-comment-use-cases.ts","lineNumber":94,"sourceCode":"};\n\nexport type EditQueuedCommentResult = {\n  queue: QueuedCommentQueueSnapshot;\n  activityPublication: QueuedCommentActivityPublication;\n};\n\nexport function createEditQueuedComment(deps: { issueLock: QueuedCommentIssueLockWriter }) {\n  return async function editQueuedComment(input: EditQueuedCommentInput): Promise<EditQueuedCommentResult> {\n    return deps.issueLock.withLockedQueue(\n      { issue: input.issue, actor: input.actor, queueId: input.queueId },\n      async (locked, tx) => {\n        requireMutationTarget(locked.queue, input.queueId, input.revision);\n        const entry = locked.queue.entries.find((candidate) => candidate.comment.id === input.commentId);\n        if (!entry) {\n          throw new QueuedCommentMutationError(\"queued_comment_not_pending\", \"The queued message is no longer pending\");\n        }\n        if (!entry.canEdit) {\n          throw new QueuedCommentMutationForbiddenError(\"Only the queued message author can edit it\");\n        }\n\n        const updated = await tx.updateCommentBody({\n          issueId: input.issue.id,\n          commentId: input.commentId,\n          body: input.body,\n          updatedAt: input.now,\n        });\n        if (!updated) {\n          throw new QueuedCommentMutationError(\"queued_comment_not_pending\", \"The queued message is no longer pending\");\n        }\n        await tx.touchIssueUpdatedAt({ issueId: input.issue.id, updatedAt: input.now });\n        await tx.syncCommentReferences(input.commentId);\n        await tx.syncCommentExternalObjectsSafely(input.commentId);\n\n        const ids = locked.queue.entries.map((candidate) => candidate.comment.id);\n        const updatedQueueRun = await updateQueueRunCommentIdsGuarded(tx, {\n          queueRun: locked.queueRun,","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/modules/wake-queue/application/queued-comment-use-cases.ts#L76-L112","documentation":"QueuedCommentMutationForbiddenError with message 'Only the queued message author can edit it', thrown from createEditQueuedComment when the targeted pending entry exists but its canEdit flag is false. canEdit is computed per actor: only the author of the queued comment may edit its body, so edits by any other actor are rejected with 403-style semantics rather than a conflict code.","triggerScenarios":"Calling editQueuedComment as an actor who is not the author of the pending queued comment — e.g. an admin or teammate trying to edit someone else's queued message, or an agent API key editing a comment queued by the board user.","commonSituations":"Operator attempting to fix a typo in a colleague's queued message; shared inbox where multiple actors view the same issue queue; UI incorrectly enabling the edit button for non-author viewers.","solutions":["Have the original author perform the edit, or have them discard it and queue a corrected message yourself","Discard your own new queued message instead of editing the other actor's entry","If the product should allow broader editing, change the canEdit computation in the queue snapshot logic — this is a policy change, not a client fix","Update the UI to render the edit control only when entry.canEdit is true"],"exampleFix":"// before\nawait editQueuedComment({ issue, actor: otherActor, queueId, revision, commentId, body });\n\n// after\nconst queue = await getQueuedComments({ issue, actor });\nconst entry = queue.entries.find((e) => e.comment.id === commentId);\nif (!entry?.canEdit) {\n  // show 'only the author can edit' and offer discard/requeue instead\n}\nawait editQueuedComment({ issue, actor, queueId, revision: queue.revision, commentId, body });","handlingStrategy":"type-guard","validationCode":"const queue = await getQueuedComments({ issue, actor });\nconst entry = queue.entries.find((e) => e.comment.id === commentId);\nif (!entry?.canEdit) throw new Error('only the author can edit this queued message');","typeGuard":"function canEditEntry(entry: { comment: { id: string }; canEdit: boolean } | undefined): entry is { comment: { id: string }; canEdit: true } {\n  return entry !== undefined && entry.canEdit;\n}","tryCatchPattern":"try {\n  await editQueuedComment(input);\n} catch (e) {\n  if (e instanceof QueuedCommentMutationForbiddenError || /author can edit/.test(e?.message ?? '')) {\n    showOnlyAuthorCanEditNotice();\n    return;\n  }\n  throw e;\n}","preventionTips":["Render edit controls only when entry.canEdit is true","Discard-and-requeue instead of editing another actor's message","Check the author of the queued comment before opening the editor","Treat this as policy, not a bug: only the author may edit"],"tags":["permission","authorization","forbidden","queue"],"backgroundTag":"permission-denied","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}