tinyhumansai/openhuman · warning

worktreeApi.diff: path is required

Error message

worktreeApi.diff: path is required

What it means

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.

Source

Thrown at app/src/services/api/worktreeApi.ts:69

    log('list');
    const view = await callCoreRpc<WorktreeListView>({
      method: 'openhuman.worktree_list',
      params: {},
    });
    log('list received count=%d overlaps=%d', view.worktrees.length, view.overlaps.length);
    return view;
  },

  /** Fetch the branch / dirty / changed-files snapshot for one worktree. */
  status: async (path: string): Promise<WorktreeStatus> => {
    if (!path.trim()) throw new Error('worktreeApi.status: path is required');
    log('status path=%s', path);
    return callCoreRpc<WorktreeStatus>({ method: 'openhuman.worktree_status', params: { path } });
  },

  /** Fetch a human-readable `git diff HEAD --stat` (plus untracked files). */
  diff: async (path: string): Promise<string> => {
    if (!path.trim()) throw new Error('worktreeApi.diff: path is required');
    log('diff path=%s', path);
    const res = await callCoreRpc<{ summary: string }>({
      method: 'openhuman.worktree_diff',
      params: { path },
    });
    return res.summary;
  },

  /**
   * Remove a worktree checkout. The core refuses a dirty worktree unless
   * `force` is `true`, so a clean worktree removes silently while a dirty one
   * rejects (the caller surfaces a confirm prompt and retries with force).
   */
  remove: async (path: string, force = false): Promise<boolean> => {
    if (!path.trim()) throw new Error('worktreeApi.remove: path is required');
    log('remove path=%s force=%s', path, force);
    const res = await callCoreRpc<{ removed: boolean }>({
      method: 'openhuman.worktree_remove',

View on GitHub (pinned to a221052e0d)

Solutions

  1. Only render/refresh the diff view when a non-empty path is available
  2. Add an early return in effects and intervals that poll the diff: if (!path.trim()) return;
  3. Filter empty strings out of any batch list before iterating with diff()
  4. Fix navigation to always carry the worktree path parameter

Example fix

// before
const res = await worktreeApi.diff(path);

// after
if (!path.trim()) return '';
const res = await worktreeApi.diff(path);
Defensive patterns

Strategy: validation

Validate before calling

if (!path?.trim()) return ''; // no target — skip the diff RPC
const summary = await worktreeApi.diff(path);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/a593cd926d5a7851. Report an issue: GitHub.