{"record":{"id":"ceb2e4e64573b374","repo":"stablyai/orca","slug":"normalizegiterrormessage-error-push-ceb2e4","errorCode":null,"errorMessage":"normalizeGitErrorMessage(error, 'push')","messagePattern":"normalizeGitErrorMessage\\(error, 'push'\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/git/remote.ts","lineNumber":210,"sourceCode":"    //\n    // When no upstream exists, keep the existing first-publish behavior:\n    // create/update origin/<current branch> and set it as upstream.\n    //\n    // Branch-vs-base reporting (the \"Committed on Branch\" section) is\n    // unaffected because it uses branchCompare against an explicit baseRef\n    // from worktree config, not the upstream relationship.\n    const target = pushTarget\n      ? explicitPushTarget(pushTarget)\n      : await getConfiguredPushTarget(worktreePath, options)\n    const args = [\n      'push',\n      ...(options.forceWithLease ? ['--force-with-lease'] : []),\n      '--set-upstream',\n      ...(target ? [target.remote, target.refspec] : ['origin', 'HEAD'])\n    ]\n    await gitExecFileAsync(args, gitOptionsForWorktree(worktreePath, options))\n  } catch (error) {\n    throw new Error(normalizeGitErrorMessage(error, 'push'))\n  }\n}\n\nasync function gitPullWithArgs(\n  worktreePath: string,\n  pullArgs: string[],\n  pushTarget?: GitPushTarget,\n  options: GitRuntimeOptions = {}\n): Promise<void> {\n  const runPull = async (effectiveArgs: string[]): Promise<void> => {\n    if (pushTarget) {\n      const target = await validateGitPushTarget(worktreePath, pushTarget, options)\n      await gitExecFileAsync(\n        ['pull', ...effectiveArgs, target.remoteName, target.branchName],\n        gitOptionsForWorktree(worktreePath, options)\n      )\n      return\n    }","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/git/remote.ts#L192-L228","documentation":"gitPush wraps its push (publish or update) in a try/catch that rethrows new Error(normalizeGitErrorMessage(error, 'push')). The literal message is the source expression; the runtime message is the normalized, credential-scrubbed push diagnostic — e.g. 'Push rejected: remote has newer commits (non-fast-forward). Please pull or sync first.', 'Authentication failed. Check your remote credentials.', 'Branch has no upstream. Publish the branch first.', a submodule-push failure detail, or the tail line of git's stderr. The push resolves the target via getConfiguredPushTarget or an explicit pushTarget, applies --set-upstream, and pushes to origin/HEAD if nothing else is configured.","triggerScenarios":"Calling gitPush(worktreePath, _publish, pushTarget, { forceWithLease }) when the remote rejects the push (non-fast-forward and forceWithLease not set or stale), credentials for the resolved remote are missing/expired, the branch has no upstream and the implicit origin/HEAD push fails, a pre-push hook non-zero-exits, or a submodule push fails (the SUBMODULE_PUSH_FAILURE patterns in normalizeGitErrorMessage).","commonSituations":"Pushing a branch that is behind origin because a collaborator force-pushed; OAuth token / SSH key for the remote expired; pushing a review branch that tracks a fork remote whose credentials are gone; a pre-push hook (lint, secret-scan) rejects the push; a recursive push fails because a submodule's remote has diverged.","solutions":["Read the normalized message — it already classifies the failure (non-fast-forward / auth / no upstream / submodule / hook / network).","For non-fast-forward: pull or sync first, or pass forceWithLease only if you intentionally intend to overwrite (and the lease tip is current).","For auth: refresh credentials for the resolved remote (the push target may be a fork remote, not origin — check getConfiguredPushTarget).","For submodule failures: pull inside the named submodule first, then retry the parent push.","For hook failures: the raw hook output is preserved in the message — fix what the hook reports."],"exampleFix":"// before\nawait gitPush(worktreePath, true)\n\n// after: pull-then-push on non-fast-forward, refresh creds on auth failure\ntry {\n  await gitPush(worktreePath, true)\n} catch (error) {\n  const msg = error instanceof Error ? error.message : ''\n  if (msg.includes('non-fast-forward')) { await gitPull(worktreePath); await gitPush(worktreePath, true) }\n  else if (msg.includes('Authentication failed')) { await refreshRemoteCreds(); await gitPush(worktreePath, true) }\n  else throw error\n}","handlingStrategy":"try-catch","validationCode":"import { getDefaultRemote } from './repo'\n\n// Pre-check that a push target can be resolved and the remote has credentials.\nconst remote = await getDefaultRemote(worktreePath)\nawait gitExecFileAsync(['ls-remote', '--exit-code', remote, 'HEAD'], gitExecOptions(worktreePath, {}))","typeGuard":"function isPushNonFastForward(error: unknown): boolean {\n  return error instanceof Error && error.message.startsWith('Push rejected: remote has newer commits')\n}\nfunction isPushNoUpstream(error: unknown): boolean {\n  return error instanceof Error && error.message.startsWith('Branch has no upstream')\n}","tryCatchPattern":"try {\n  await gitPush(worktreePath, true, pushTarget, options)\n} catch (error) {\n  if (isPushNonFastForward(error)) { await gitPull(worktreePath, undefined, options); await gitPush(worktreePath, true, pushTarget, options) }\n  else if (isPushNoUpstream(error)) { await gitPush(worktreePath, true, undefined, options) /* first publish */ }\n  else throw error\n}","preventionTips":["Pull or sync before pushing if the branch may be behind origin.","Refresh remote credentials before retry on auth-class failures.","Use the normalized message to pick the retry strategy rather than matching raw git stderr."],"tags":["git","push","remote","normalized-error"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}