{"record":{"id":"79756847af983b1d","repo":"stablyai/orca","slug":"failed-to-save-review-notes","errorCode":null,"errorMessage":"Failed to save review notes","messagePattern":"Failed to save review notes","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/app/h/[hostId]/session/[worktreeId].tsx","lineNumber":1953,"sourceCode":"      return\n    }\n    const result = (response as RpcSuccess).result as {\n      worktree?: { diffComments?: unknown }\n    }\n    setDiffComments(normalizeMobileDiffComments(result.worktree?.diffComments, worktreeId))\n  }, [client, connState, worktreeId, isFloatingWorkspaceRoute])\n\n  const persistDiffComments = useCallback(\n    async (comments: readonly DiffComment[]): Promise<void> => {\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      })\n      if (!response.ok) {\n        throw new Error((response as RpcFailure).error.message || 'Failed to save review notes')\n      }\n    },\n    [client, connState, worktreeId]\n  )\n\n  useEffect(() => {\n    void loadDiffComments()\n  }, [loadDiffComments])\n\n  const addDiffCommentForFile = useCallback(\n    async (filePath: string, lineNumber: number, body: string): Promise<boolean> => {\n      if (diffCommentBusy) {\n        return false\n      }\n      const nextId = `mobile-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n      const result = addMobileDiffComment(diffCommentsRef.current, {\n        id: nextId,\n        worktreeId,","sourceCodeStart":1935,"sourceCodeEnd":1971,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/app/h/[hostId]/session/[worktreeId].tsx#L1935-L1971","documentation":"Thrown in persistDiffComments() when the worktree.set RPC returns a failure response (ok: false) AND the failure has no error.message. This is the fallback error string when the server-side error message is empty or missing. The RPC was sent and a response received, but the server rejected the diff comment save without a descriptive error.","triggerScenarios":"client.sendRequest('worktree.set', { worktree, diffComments }) returns ok:false with error.message being empty/undefined. Caused by: the desktop host rejecting the diff comments payload (schema mismatch, worktree not found, ownership conflict) but returning an error with no message field.","commonSituations":"A version skew between mobile client and desktop host where the diffComments schema changed; a worktree that was removed server-side; a mutation ownership conflict where another client holds the lock; a server bug returning an error object without a message.","solutions":["Check the desktop host logs for the actual worktree.set rejection reason.","Verify the diffComments payload matches the schema expected by the desktop host version.","If error.message is empty, log the full failure object (code, details) to diagnose the server-side rejection.","Ensure mutation ownership is captured before the worktree.set call."],"exampleFix":"// before: empty server message gives generic error\nthrow new Error((response as RpcFailure).error.message || 'Failed to save review notes')\n\n// after: include error code for diagnosability\nconst failure = response as RpcFailure\nthrow new Error(\n  failure.error.message ||\n  `Failed to save review notes (code: ${failure.error.code ?? 'unknown'})`\n)","handlingStrategy":"try-catch","validationCode":"function buildPersistErrorMessage(response) {\n  if (response.ok) return null\n  const failure = response as RpcFailure\n  return failure.error.message || `Failed to save review notes (code: ${failure.error.code ?? 'unknown'})`\n}","typeGuard":"function isRpcFailureWithMessage(response) {\n  return !response.ok && typeof response.error?.message === 'string' &&\n    response.error.message.length > 0\n}","tryCatchPattern":"try {\n  await persistDiffComments(comments)\n} catch (err) {\n  if (err.message === 'Failed to save review notes') {\n    // Server returned an error with no message — log the full failure for diagnosis\n    console.error('worktree.set failed with no message — check host logs')\n  }\n  showToast(err.message, 1800)\n}","preventionTips":["Include the RPC error code in the fallback message for diagnosability.","Ensure the desktop host always returns a descriptive error.message for worktree.set failures.","Log the full failure object client-side when the message is empty."],"tags":["mobile","rpc","review-notes","worktree","server-error"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}