{"record":{"id":"befa8a7b135f92ba","repo":"tinyhumansai/openhuman","slug":"worktreeapi-status-path-is-required","errorCode":null,"errorMessage":"worktreeApi.status: path is required","messagePattern":"worktreeApi\\.status: path is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/src/services/api/worktreeApi.ts","lineNumber":62,"sourceCode":"  worktrees: WorktreeStatus[];\n  overlaps: WorktreeOverlap[];\n}\n\nexport const worktreeApi = {\n  /** List managed worker worktrees plus cross-worktree file overlaps. */\n  list: async (): Promise<WorktreeListView> => {\n    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","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/worktreeApi.ts#L44-L80","documentation":"A client-side precondition guard in worktreeApi.status(): it trims the path argument and throws before any RPC when empty. 'openhuman.worktree_status' addresses one checkout by path; an empty path is always a caller bug (no worktree selected), so the guard fails fast with a descriptive message.","triggerScenarios":"A worktree detail panel or keyboard shortcut firing before a worktree is selected; a selectedWorktree state that is '' on first render; a list-to-detail data flow passing an unpopulated binding.","commonSituations":"UI race where the detail view mounts before selection propagates; effects that run on mount with default empty state; refactors that renamed the path field leaving the binding empty.","solutions":["Gate the action on selection: only call status() when a non-empty path is selected","Disable the detail view / show an empty state while selectedPath is blank","Guard effects: if (!path.trim()) return; as the first line of any effect that calls this","Check the component's state wiring if it fires with '' — the selection binding is the bug"],"exampleFix":"// before\nuseEffect(() => { worktreeApi.status(path); }, [path]);\n\n// after\nuseEffect(() => {\n  if (!path.trim()) return;\n  worktreeApi.status(path);\n}, [path]);","handlingStrategy":"validation","validationCode":"if (!path?.trim()) {\n  setSelectedWorktree(null); // nothing selected — do not call status()\n  return;\n}\nawait worktreeApi.status(path);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Gate worktree detail effects on a non-empty selected path","Model selection as null | path, not '' — makes empty unrepresentable","Disable refresh controls while the selection is empty"],"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"}