stablyai/orca · error

Invalid branch name

Error message

Invalid branch name

What it means

Thrown at the top of forceDeleteLocalBranch when branchName is falsy (empty string, undefined, null) OR contains a NUL byte (`\0`). The NUL check is an argv-injection guard: git uses NUL as a record separator in porcelain output and a NUL in a branch name could smuggle additional ref arguments into `update-ref`. This is a programmer-error guard, not a recoverable runtime condition.

Source

Thrown at src/main/git/worktree.ts:1385

    return false
  }
  await forceDeleteLocalBranch(repoPath, branchName, branchHead, (args, cwd) =>
    gitExecFileAsync(args, gitExecOptions(cwd, options))
  )
  return true
}

export async function forceDeleteLocalBranch(
  repoPath: string,
  branchName: string,
  expectedHead: string,
  runGit: (args: string[], cwd: string) => Promise<{ stdout: string; stderr: string }> = (
    args,
    cwd
  ) => gitExecFileAsync(args, { cwd })
): Promise<void> {
  if (!branchName || branchName.includes('\0')) {
    throw new Error('Invalid branch name')
  }
  if (!expectedHead) {
    throw new Error(
      `Cannot force-delete local branch "${branchName}" without the commit Git preserved.`
    )
  }
  if (await isLocalBranchCheckedOut(repoPath, branchName, runGit)) {
    throw new Error(`Local branch "${branchName}" is checked out in another worktree.`)
  }
  // Why: stale toast actions must not delete a branch that moved; `update-ref -d` deletes only if the ref still == expectedHead.
  try {
    await runGit(['update-ref', '-d', `refs/heads/${branchName}`, expectedHead], repoPath)
  } catch {
    throw new Error(
      `Local branch "${branchName}" changed after the workspace was deleted. Review it before deleting it.`
    )
  }
  if (await isLocalBranchCheckedOut(repoPath, branchName, runGit)) {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Inspect the call site: ensure branchName is always a non-empty string before invoking forceDeleteLocalBranch.
  2. If the value is coming from a serialized toast action, validate and discard the action when branchName is empty rather than calling through.
  3. Sanitize ref names at the trust boundary — reject any containing NUL, whitespace, or `..`/`~`/`^`/`:` per git-check-ref-format before they reach this function.

Example fix

// before
await forceDeleteLocalBranch(repoPath, maybeBranch, head)

// after
if (!maybeBranch || maybeBranch.includes('\0')) {
  discardStaleToast()
  return
}
await forceDeleteLocalBranch(repoPath, maybeBranch, head)
Defensive patterns

Strategy: validation

Validate before calling

function isValidBranchNameForDelete(name: unknown): name is string {
  return typeof name === 'string' && name.length > 0 && !name.includes('\0')
}

Type guard

function isValidBranchNameForDelete(name: unknown): name is string {
  return typeof name === 'string' && name.length > 0 && !name.includes('\0')
}

Prevention

When it happens

Trigger: A stale toast action firing after the branch name was cleared from UI state; a serialized action whose branch field was never populated; a malicious or malformed ref name reaching the function via a crafted remote; testing code that passes undefined by mistake.

Common situations: UI 'delete branch' toast surviving a workspace reload that nulled the branch field; a race where the worktree is removed before the branch-delete action captures its name; fuzzed/malicious input attempting argument injection.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/a0a6821eecf90166. Report an issue: GitHub.