stablyai/orca · error
normalizeGitErrorMessage(error, 'push')
Error message
normalizeGitErrorMessage(error, 'push')
What it means
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.
Source
Thrown at src/main/git/remote.ts:210
//
// When no upstream exists, keep the existing first-publish behavior:
// create/update origin/<current branch> and set it as upstream.
//
// Branch-vs-base reporting (the "Committed on Branch" section) is
// unaffected because it uses branchCompare against an explicit baseRef
// from worktree config, not the upstream relationship.
const target = pushTarget
? explicitPushTarget(pushTarget)
: await getConfiguredPushTarget(worktreePath, options)
const args = [
'push',
...(options.forceWithLease ? ['--force-with-lease'] : []),
'--set-upstream',
...(target ? [target.remote, target.refspec] : ['origin', 'HEAD'])
]
await gitExecFileAsync(args, gitOptionsForWorktree(worktreePath, options))
} catch (error) {
throw new Error(normalizeGitErrorMessage(error, 'push'))
}
}
async function gitPullWithArgs(
worktreePath: string,
pullArgs: string[],
pushTarget?: GitPushTarget,
options: GitRuntimeOptions = {}
): Promise<void> {
const runPull = async (effectiveArgs: string[]): Promise<void> => {
if (pushTarget) {
const target = await validateGitPushTarget(worktreePath, pushTarget, options)
await gitExecFileAsync(
['pull', ...effectiveArgs, target.remoteName, target.branchName],
gitOptionsForWorktree(worktreePath, options)
)
return
}View on GitHub (pinned to 1136503c6a)
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.
Example fix
// before
await gitPush(worktreePath, true)
// after: pull-then-push on non-fast-forward, refresh creds on auth failure
try {
await gitPush(worktreePath, true)
} catch (error) {
const msg = error instanceof Error ? error.message : ''
if (msg.includes('non-fast-forward')) { await gitPull(worktreePath); await gitPush(worktreePath, true) }
else if (msg.includes('Authentication failed')) { await refreshRemoteCreds(); await gitPush(worktreePath, true) }
else throw error
} Defensive patterns
Strategy: try-catch
Validate before calling
import { getDefaultRemote } from './repo'
// Pre-check that a push target can be resolved and the remote has credentials.
const remote = await getDefaultRemote(worktreePath)
await gitExecFileAsync(['ls-remote', '--exit-code', remote, 'HEAD'], gitExecOptions(worktreePath, {})) Type guard
function isPushNonFastForward(error: unknown): boolean {
return error instanceof Error && error.message.startsWith('Push rejected: remote has newer commits')
}
function isPushNoUpstream(error: unknown): boolean {
return error instanceof Error && error.message.startsWith('Branch has no upstream')
} Try / catch
try {
await gitPush(worktreePath, true, pushTarget, options)
} catch (error) {
if (isPushNonFastForward(error)) { await gitPull(worktreePath, undefined, options); await gitPush(worktreePath, true, pushTarget, options) }
else if (isPushNoUpstream(error)) { await gitPush(worktreePath, true, undefined, options) /* first publish */ }
else throw error
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- normalizeGitErrorMessage(error, 'push')
- normalizeGitErrorMessage(error, 'pull')
- normalizeGitErrorMessage(error, 'fetch')
- Repo has no configured git remotes.
- Repo has multiple remotes (${remotes.join(', ')}) and no def
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/ceb2e4e64573b374.
Report an issue: GitHub.