{"record":{"id":"216f26dcc0057f83","repo":"stablyai/orca","slug":"normalizegiterrormessage-error-pull","errorCode":null,"errorMessage":"normalizeGitErrorMessage(error, 'pull')","messagePattern":"normalizeGitErrorMessage\\(error, 'pull'\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/git/remote.ts","lineNumber":248,"sourceCode":"      gitExecFileAsync(args, gitOptionsForWorktree(worktreePath, options))\n    )\n    if (upstream && !upstream.isConfiguredUpstream) {\n      // Why: legacy Orca branches may still track origin/main while pushes\n      // target origin/<branch>. Pull the same effective branch the UI reports.\n      await gitExecFileAsync(\n        ['pull', ...effectiveArgs, upstream.remoteName, upstream.branchName],\n        gitOptionsForWorktree(worktreePath, options)\n      )\n      return\n    }\n\n    await gitExecFileAsync(['pull', ...effectiveArgs], gitOptionsForWorktree(worktreePath, options))\n  }\n\n  try {\n    await runPullWithDivergenceFallback(pullArgs, runPull)\n  } catch (error) {\n    throw new Error(normalizeGitErrorMessage(error, 'pull'))\n  }\n}\n\nexport async function gitPull(\n  worktreePath: string,\n  pushTarget?: GitPushTarget,\n  options: GitRuntimeOptions = {}\n): Promise<void> {\n  // Why: plain `git pull` uses the user's configured pull strategy (merge by\n  // default) so diverged branches reconcile instead of erroring out. Conflicts\n  // surface through the existing conflict-resolution flow.\n  await runWithGitReadCacheInvalidation(() =>\n    gitPullWithArgs(worktreePath, [], pushTarget, options)\n  )\n}\n\nexport async function gitFastForward(\n  worktreePath: string,","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/git/remote.ts#L230-L266","documentation":"gitPullWithArgs wraps its pull (used by gitPull and gitFastForward) so that after runPullWithDivergenceFallback exhausts its merge-retry fallback, any remaining failure is rethrown as new Error(normalizeGitErrorMessage(error, 'pull')). The literal message is the source expression; the runtime message is the normalized pull diagnostic — e.g. 'Pull needs a Git pull policy for divergent branches...' (only if the fallback could not apply), 'Pull would overwrite local changes...', 'Pull would overwrite untracked files...', 'Authentication failed.', or the tail stderr line.","triggerScenarios":"Calling gitPull or gitFastForward when local working-tree changes would be overwritten by the incoming merge, untracked files collide with fetched paths, credentials are missing, the divergence-fallback itself fails (e.g. pullArgs already specified --ff-only so the --no-rebase fallback was skipped), or the upstream is unreachable.","commonSituations":"Pulling into a dirty worktree with conflicting tracked files; pulling a branch that adds files already present as untracked; a repo with no pull.rebase/pull.ff policy where the --ff-only fast-forward path (gitFastForward) cannot apply the merge fallback because pullArgsSpecifyReconciliation returned true; expired remote credentials; reviewer pulls a stale review branch whose upstream was force-pushed.","solutions":["Read the normalized message — it pins the failure class (overwrites untracked/working-tree, divergence policy, auth, network).","For 'would overwrite local changes': commit, stash, or discard the listed files before pulling.","For 'would overwrite untracked files': move, remove, or git-add the colliding untracked paths first.","For divergence-policy on a fast-forward pull: the --ff-only path intentionally skips the merge fallback — use gitPull (plain) instead of gitFastForward when divergence is expected.","For auth: refresh credentials for the resolved upstream remote."],"exampleFix":"// before\nawait gitFastForward(worktreePath) // --ff-only; no merge fallback\n\n// after: use plain pull when divergence is possible so the merge fallback can apply\nawait gitPull(worktreePath)","handlingStrategy":"try-catch","validationCode":"import { isDirtyWorktree, listUntrackedPaths } from './status'\n\n// Pre-check: pull cannot overwrite a dirty worktree.\nif (await isDirtyWorktree(worktreePath)) {\n  throw new Error('Commit or stash local changes before pulling.')\n}","typeGuard":"function isPullOverwriteLocal(error: unknown): boolean {\n  return error instanceof Error && error.message.startsWith('Pull would overwrite local changes')\n}\nfunction isPullOverwriteUntracked(error: unknown): boolean {\n  return error instanceof Error && error.message.startsWith('Pull would overwrite untracked files')\n}","tryCatchPattern":"try {\n  await gitPull(worktreePath, pushTarget, options)\n} catch (error) {\n  if (isPullOverwriteLocal(error)) { await stashAll(worktreePath); await gitPull(worktreePath, pushTarget, options); await popStash(worktreePath) }\n  else if (isPullOverwriteUntracked(error)) { await moveUntrackedAside(worktreePath); await gitPull(worktreePath, pushTarget, options) }\n  else throw error\n}","preventionTips":["Prefer gitPull over gitFastForward when divergence is possible — only the plain pull path can use the merge fallback.","Stash or commit dirty changes before pulling.","Move colliding untracked files out of the way before pulling a branch that adds them."],"tags":["git","pull","remote","normalized-error","divergence"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}