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

  1. Read the normalized message — it already classifies the failure (non-fast-forward / auth / no upstream / submodule / hook / network).
  2. For non-fast-forward: pull or sync first, or pass forceWithLease only if you intentionally intend to overwrite (and the lease tip is current).
  3. For auth: refresh credentials for the resolved remote (the push target may be a fork remote, not origin — check getConfiguredPushTarget).
  4. For submodule failures: pull inside the named submodule first, then retry the parent push.
  5. 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

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


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