stablyai/orca · error

normalizeGitErrorMessage(error, 'fetch')

Error message

normalizeGitErrorMessage(error, 'fetch')

What it means

gitFetch wraps its fetch --prune in a try/catch that rethrows new Error(normalizeGitErrorMessage(error, 'fetch')). The literal message is the source expression; the runtime message is the normalized fetch diagnostic. Because the operation is 'fetch' (not 'push'/'pull'), the push-specific non-fast-forward and submodule hints are skipped — you get auth, network, no-upstream, or the tail stderr line.

Source

Thrown at src/main/git/remote.ts:312

}

export async function gitFetch(
  worktreePath: string,
  pushTarget?: GitPushTarget,
  options: GitRuntimeOptions = {}
): Promise<void> {
  try {
    if (pushTarget) {
      const target = await validateGitPushTarget(worktreePath, pushTarget, options)
      await gitExecFileAsync(
        ['fetch', '--prune', target.remoteName],
        gitOptionsForWorktree(worktreePath, options)
      )
      return
    }
    await gitExecFileAsync(['fetch', '--prune'], gitOptionsForWorktree(worktreePath, options))
  } catch (error) {
    throw new Error(normalizeGitErrorMessage(error, 'fetch'))
  }
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Read the normalized message — auth, network, and no-upstream hints are pre-classified.
  2. For auth: refresh credentials for the target remote (or origin when no pushTarget is set).
  3. For network: retry once; fetch is idempotent and --prune is safe to re-run.
  4. Verify the target remote exists (git remote) before fetching when pushTarget is supplied — a removed fork remote fails here.

Example fix

// before
await gitFetch(worktreePath, { remoteName: 'fork', branchName: 'feat' })

// after: verify the remote exists, retry once on transient network failure
const remotes = await readRemotes(worktreePath)
if (!remotes.includes('fork')) throw new Error('Configure the fork remote first')
try { await gitFetch(worktreePath, { remoteName: 'fork', branchName: 'feat' }) }
catch (error) {
  if (/(Network error|Could not resolve host)/.test(String(error))) await gitFetch(worktreePath, { remoteName: 'fork', branchName: 'feat' })
  else throw error
}
Defensive patterns

Strategy: retry

Validate before calling

import { getDefaultRemote } from './repo'

// Pre-check the target remote exists before fetching.
const { stdout } = await gitExecFileAsync(['remote'], gitExecOptions(worktreePath, {}))
const remotes = stdout.split('\n').map((l) => l.trim()).filter(Boolean)
const target = pushTarget?.remoteName ?? await getDefaultRemote(worktreePath).catch(() => null)
if (target && !remotes.includes(target)) throw new Error(`Remote '${target}' is not configured.`)

Type guard

function isFetchAuth(error: unknown): boolean {
  return error instanceof Error && error.message.startsWith('Authentication failed')
}
function isFetchNetwork(error: unknown): boolean {
  return error instanceof Error && error.message.startsWith('Network error')
}

Try / catch

try {
  await gitFetch(worktreePath, pushTarget, options)
} catch (error) {
  if (isFetchNetwork(error)) { await gitFetch(worktreePath, pushTarget, options) /* idempotent, safe to retry once */ }
  else if (isFetchAuth(error)) { await refreshRemoteCreds(worktreePath); await gitFetch(worktreePath, pushTarget, options) }
  else throw error
}

Prevention

When it happens

Trigger: Calling gitFetch(worktreePath, pushTarget, options) when the remote is unreachable, credentials are missing/expired, the named remote (pushTarget.remoteName) does not exist, the network drops mid-fetch, or a prune references a remote ref that is concurrently being deleted.

Common situations: Expired OAuth token / SSH key for the remote; offline or flaky network; a pushTarget pointing at a fork remote that was removed; fetching right after a remote rename without updating pushTarget; large repos on slow links hitting exec-level stalls.

Related errors


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