{"record":{"id":"a593cd926d5a7851","repo":"tinyhumansai/openhuman","slug":"worktreeapi-diff-path-is-required","errorCode":null,"errorMessage":"worktreeApi.diff: path is required","messagePattern":"worktreeApi\\.diff: path is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/src/services/api/worktreeApi.ts","lineNumber":69,"sourceCode":"    log('list');\n    const view = await callCoreRpc<WorktreeListView>({\n      method: 'openhuman.worktree_list',\n      params: {},\n    });\n    log('list received count=%d overlaps=%d', view.worktrees.length, view.overlaps.length);\n    return view;\n  },\n\n  /** Fetch the branch / dirty / changed-files snapshot for one worktree. */\n  status: async (path: string): Promise<WorktreeStatus> => {\n    if (!path.trim()) throw new Error('worktreeApi.status: path is required');\n    log('status path=%s', path);\n    return callCoreRpc<WorktreeStatus>({ method: 'openhuman.worktree_status', params: { path } });\n  },\n\n  /** Fetch a human-readable `git diff HEAD --stat` (plus untracked files). */\n  diff: async (path: string): Promise<string> => {\n    if (!path.trim()) throw new Error('worktreeApi.diff: path is required');\n    log('diff path=%s', path);\n    const res = await callCoreRpc<{ summary: string }>({\n      method: 'openhuman.worktree_diff',\n      params: { path },\n    });\n    return res.summary;\n  },\n\n  /**\n   * Remove a worktree checkout. The core refuses a dirty worktree unless\n   * `force` is `true`, so a clean worktree removes silently while a dirty one\n   * rejects (the caller surfaces a confirm prompt and retries with force).\n   */\n  remove: async (path: string, force = false): Promise<boolean> => {\n    if (!path.trim()) throw new Error('worktreeApi.remove: path is required');\n    log('remove path=%s force=%s', path, force);\n    const res = await callCoreRpc<{ removed: boolean }>({\n      method: 'openhuman.worktree_remove',","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/worktreeApi.ts#L51-L87","documentation":"A client-side precondition guard in worktreeApi.diff(): identical pattern to status() — trims the path and throws before the 'openhuman.worktree_diff' RPC when empty. The diff view needs a concrete checkout path; an empty one is a selection bug, not a core failure.","triggerScenarios":"The diff panel refreshing on an empty selection (initial mount, after deselect), a refresh-all loop iterating a list containing an empty path entry, or a stale closure capturing '' before selection lands.","commonSituations":"Diff panel mounted before the worktree list loads; polling refresh keyed to a path that resets to '' on refetch; programmatic navigation to the diff view without a path parameter.","solutions":["Only render/refresh the diff view when a non-empty path is available","Add an early return in effects and intervals that poll the diff: if (!path.trim()) return;","Filter empty strings out of any batch list before iterating with diff()","Fix navigation to always carry the worktree path parameter"],"exampleFix":"// before\nconst res = await worktreeApi.diff(path);\n\n// after\nif (!path.trim()) return '';\nconst res = await worktreeApi.diff(path);","handlingStrategy":"validation","validationCode":"if (!path?.trim()) return ''; // no target — skip the diff RPC\nconst summary = await worktreeApi.diff(path);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Early-return in effects/intervals that poll the diff when the path is blank","Filter empty paths out of batch iterations before calling diff()","Reset diff state to 'empty' whenever selection clears so the UI never requests ''"],"tags":["validation","precondition","worktree","ui"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}