{"record":{"id":"cfc9218ee152b45e","repo":"stablyai/orca","slug":"failed-to-save-review-state","errorCode":null,"errorMessage":"Failed to save review state","messagePattern":"Failed to save review state","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/session/use-mobile-diff-review-comment-actions.ts","lineNumber":75,"sourceCode":"    setCurrentIndex,\n    setComposer,\n    setComposerBody,\n    setActionError,\n    setShowCompletion\n  } = input\n\n  const persistMetadata = useCallback(\n    async (comments: readonly DiffComment[], reviewState: MobileDiffReviewState) => {\n      if (!client || connState !== 'connected') {\n        throw new Error('Waiting for desktop...')\n      }\n      const response = await client.sendRequest('worktree.set', {\n        worktree: `id:${worktreeId}`,\n        diffComments: comments,\n        mobileDiffReview: reviewState\n      })\n      if (!response.ok) {\n        throw new Error(response.error?.message || 'Failed to save review state')\n      }\n    },\n    [client, connState, worktreeId]\n  )\n\n  const updateReadyState = useCallback(\n    (updater: (state: Extract<ReviewScreenState, { kind: 'ready' }>) => ReviewScreenState) => {\n      setScreenState((prev) => (prev.kind === 'ready' ? updater(prev) : prev))\n    },\n    [setScreenState]\n  )\n\n  const saveCommentsAndReviewState = useCallback(\n    async (comments: DiffComment[], reviewState: MobileDiffReviewState) => {\n      const previous = screenState\n      updateReadyState((state) => ({ ...state, comments, reviewState }))\n      try {\n        await persistMetadata(comments, reviewState)","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/session/use-mobile-diff-review-comment-actions.ts#L57-L93","documentation":"Thrown by `persistMetadata` when `worktree.set` (persisting diffComments + mobileDiffReview) returns ok:false. The host's error.message is preferred; the literal is the fallback. The surrounding `saveCommentsAndReviewState` rolls back the optimistic state and re-throws, so the UI reverts to the prior review state.","triggerScenarios":"The host rejects the metadata write - worktree gone, disk write failure, schema validation on the host, or a transport error. The optimistic comment/review-state change was already applied to local state and will be rolled back.","commonSituations":"Worktree deleted on host mid-review; host settings/worktree metadata store locked or corrupt; relay cutover rejects the one-shot; large comment payload exceeds a host limit.","solutions":["Retry the save after reconnecting.","Confirm the worktree still exists.","Reduce comment payload / check for invalid characters.","Check host logs for the worktree.set error."],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(response.error?.message || 'Failed to save review state')\n}\n// after\nif (!response.ok) {\n  throw new RpcMethodError('worktree.set', response.error)\n}","handlingStrategy":"try-catch","validationCode":"if (!client || connState !== 'connected') {\n  setActionError('Waiting for desktop...')\n  return\n}","typeGuard":"function isRpcFailure(r: RpcResponse): r is RpcFailure {\n  return r.ok === false\n}","tryCatchPattern":"try {\n  await persistMetadata(comments, reviewState)\n} catch (err) {\n  // optimistic state already rolled back by saveCommentsAndReviewState\n  setActionError(err instanceof Error ? err.message : 'Failed to save review')\n}","preventionTips":["Keep mutations small to stay under host limits.","Retry once on relay cutover before surfacing the error.","Confirm worktree existence before enabling review actions."],"tags":["rpc","worktree-set","diff-review","mobile"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}