{"record":{"id":"ac2e7ede529240ca","repo":"tinyhumansai/openhuman","slug":"comment-deletion-was-not-accepted-by-the-backend","errorCode":null,"errorMessage":"Comment deletion was not accepted by the backend","messagePattern":"Comment deletion was not accepted by the backend","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/agentworld/pages/FeedSection.tsx","lineNumber":612,"sourceCode":"  postId,\n  onCommentDeleted,\n}: {\n  comment: GqlComment;\n  myAgentId: string | null;\n  handle: string;\n  postId: string;\n  onCommentDeleted: () => void;\n}) {\n  // Drives the in-app confirm modal for comment deletion (#4197).\n  const [confirmingDelete, setConfirmingDelete] = useState(false);\n  const [deleting, setDeleting] = useState(false);\n\n  const confirmDeleteComment = () => {\n    setDeleting(true);\n    void apiClient.feeds\n      .deleteComment(handle, postId, comment.commentId)\n      .then(({ ok }) => {\n        if (!ok) throw new Error('Comment deletion was not accepted by the backend');\n        onCommentDeleted();\n      })\n      .catch(err => console.error('[FeedSection] delete comment failed:', err))\n      .finally(() => {\n        setDeleting(false);\n        setConfirmingDelete(false);\n      });\n  };\n\n  return (\n    <div className=\"flex gap-3 py-3\">\n      {comment.author.avatarUrl ? (\n        <img\n          src={comment.author.avatarUrl}\n          alt={comment.author.displayName}\n          className=\"h-7 w-7 shrink-0 rounded-full object-cover\"\n        />\n      ) : (","sourceCodeStart":594,"sourceCodeEnd":630,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/agentworld/pages/FeedSection.tsx#L594-L630","documentation":"Thrown in the Agent World feed comment-delete confirm modal (#4197) when apiClient.feeds.deleteComment(handle, postId, comment.commentId) resolves with { ok: false } - the backend received the request but refused the deletion. This is a soft failure (HTTP-level success, application-level refusal), not a network error; both land in the same .catch which only console.errors, so the comment silently stays in the UI.","triggerScenarios":"User clicks Delete on a comment and confirms, and the feeds backend returns ok:false - e.g. the comment was already deleted from another session/window, the actor's handle lacks delete permission on that feed, or the postId/commentId passed from stale feed state no longer match a live row.","commonSituations":"Two windows/two devices on the same feed where one already deleted the comment; feed state left stale after a reconnect or account switch (old handle no longer authorized); backend implemented to answer refusals as 200 + ok:false rather than 4xx, so nothing surfaces except the console.","solutions":["Refetch the feed (homeFeed) and retry deletion only if the comment still appears - the usual cause is a stale commentId","Verify the handle/postId/commentId trio matches the rendered comment (not data left over from pagination)","Inspect the network response body for deleteComment - ok:false is opaque by design, but the payload may carry a reason field","If it reproduces reliably, extend deleteComment to surface a reason and show a user-visible toast instead of console.error only"],"exampleFix":"// before\n.then(({ ok }) => {\n  if (!ok) throw new Error('Comment deletion was not accepted by the backend');\n  onCommentDeleted();\n})\n.catch(err => console.error('[FeedSection] delete comment failed:', err));\n\n// after - surface the failure and resync instead of only logging\n.then(({ ok }) => {\n  if (!ok) throw new Error('Comment deletion was not accepted by the backend');\n  onCommentDeleted();\n})\n.catch(err => {\n  console.error('[FeedSection] delete comment failed:', err);\n  setDeleteError(err instanceof Error ? err.message : String(err)); // banner in the modal\n  onCommentDeleted(); // reuse the refresh callback to drop stale comments\n});","handlingStrategy":"try-catch","validationCode":"// Before deleting, confirm the comment is still in the loaded feed state\n// (stale ids after deletion elsewhere are the top cause of ok:false).\nconst stillPresent = feedState.comments.some(c => c.commentId === comment.commentId);\nif (!stillPresent) { onCommentDeleted(); return; } // treat as already deleted","typeGuard":null,"tryCatchPattern":"// keep the .catch but make it user-visible and resync on failure\nvoid apiClient.feeds.deleteComment(handle, postId, comment.commentId)\n  .then(({ ok }) => {\n    if (!ok) throw new Error('Comment deletion was not accepted by the backend');\n    onCommentDeleted();\n  })\n  .catch(err => {\n    console.error('[FeedSection] delete comment failed:', err);\n    setDeleteError(err instanceof Error ? err.message : String(err));\n    onCommentDeleted(); // refresh so the UI converges with the backend\n  });","preventionTips":["Treat ok:false as a first-class outcome of every feeds mutation - check it explicitly rather than assuming resolve means success","Always run a feed refresh after a failed delete so stale rows drop out of the UI","Never reuse commentId values captured before a pagination/refetch cycle"],"tags":["agentworld","feed","comment","deletion","soft-failure","api-client"],"backgroundTag":"mutation-rejected-by-backend","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}