gitbutlerapp/gitbutler · error

push failed after {MAX_RETRIES} attempts

Error message

push failed after {MAX_RETRIES} attempts

What it means

The publish push loop retries non-fast-forward conflicts up to MAX_RETRIES (5), calling `resolve_push_conflict` between attempts. This bail means every attempt still hit a non-fast-forward rejection — the remote kept moving faster than conflicts could be resolved. It indicates continuous concurrent writing to the GitMeta remote, or conflict resolution that never converges.

Source

Thrown at crates/but-agentlog/src/gitmeta/write.rs:301

        Err(git_meta_lib::Error::GitCommand(message))
            if message.contains("couldn't find remote ref") => {}
        Err(err) => return Err(err).context("failed to pull GitMeta metadata"),
    }

    let mut attempts = 0;
    loop {
        attempts += 1;
        let output = gitmeta
            .push_once(None)
            .context("failed to push GitMeta metadata")?;
        if output.success {
            return Ok(());
        }
        if !output.non_fast_forward {
            bail!("push failed");
        }
        if attempts >= MAX_RETRIES {
            bail!("push failed after {MAX_RETRIES} attempts");
        }
        gitmeta
            .resolve_push_conflict(None)
            .context("failed to resolve GitMeta push conflict")?;
    }
}

fn stored_text(kind: RecordKind, text: Option<&str>) -> Option<String> {
    let text = text?;
    Some(match kind {
        RecordKind::ToolResult => redact_text(cap_tool_result_text(text).as_ref()),
        _ => redact_text(text),
    })
}

fn file_path_hashes_for_record(
    repo_root: &Path,
    tool_kind: Option<ToolKind>,

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Wait briefly and re-run `but agentlog sync` then the publish — record dedup makes the retry idempotent and the contention usually clears
  2. Stagger publishes (run one machine's agentlog at a time, or serialize capture/publish in CI) so writers stop racing
  3. If it never converges, inspect the GitMeta ref divergence (`git log` on the metadata remote ref) — a one-time manual reset of the local metadata ref to the remote head, then re-publish, breaks the cycle
  4. Report non-converging resolve_push_conflict behavior upstream with the metadata ref graphs if a manual reset also fails

Example fix

// before: concurrent publishers race until exhaustion
publish_session(&repo, &session)?;

// after: backoff-and-retry at the caller level
let mut delay = Duration::from_secs(5);
let result = loop {
    match publish_session(&repo, &session) {
        Ok(()) => break Ok(()),
        Err(e) if e.to_string().contains("after 5 attempts") && delay < Duration::from_secs(60) => {
            std::thread::sleep(delay);
            delay *= 2;
        }
        Err(e) => break Err(e),
    }
};
Defensive patterns

Strategy: fallback

Validate before calling

// Detect sustained contention cheaply before publishing in shared setups
let remote_head = gitmeta.remote_head_oid(None)?; // diverged head predicts conflict storms
if locally_diverged_beyond(&remote_head, 3) {
    eprintln!("metadata ref heavily diverged; run `but agentlog sync` first");
}

Type guard

fn conflicts_exhausted(err: &anyhow::Error) -> bool {
    err.to_string().contains("push failed after 5 attempts")
}

Try / catch

match publish(&repo, &session) {
    Ok(()) => {}
    Err(err) if err.to_string().contains("after 5 attempts") => {
        // proceed without publishing now; re-publish later — dedup makes it safe
        queue_for_retry_later(session);
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Multiple agents/machines running `but agentlog publish`/`sync` against the same GitMeta remote simultaneously, so each resolve-push-conflict cycle finds yet another newer remote state; or conflict resolution that cannot produce a fast-forwardable state (diverged histories).

Common situations: Several parallel agent sessions finishing and publishing at once in CI or a shared repo; two workstations with heavy agentlog activity syncing the same metadata remote; a long-lived divergence in the metadata ref that auto-resolution cannot merge.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/0dfe051cfa539344. Report an issue: GitHub.