can1357/oh-my-pi · error · Error

cli_message(command, exit_code, stdout, stderr)

Error message

cli_message(command, exit_code, stdout, stderr)

What it means

Error::Cli is raised when a CLI-backed operation (push/fetch/clone, reftable fallback) exits non-zero. It carries the rendered command line, exit code, and captured stdout/stderr (possibly truncated) so user-facing error surfaces can show exactly what git/jj printed.

Source

Thrown at crates/pi-vcs/src/error.rs:64

	/// A merge-style operation (cherry-pick, stash pop, 3-way apply) hit
	/// conflicting changes.
	#[error("merge conflict in {} file(s)", paths.len())]
	Conflict {
		/// Worktree-relative paths left in a conflicted state.
		paths: Vec<String>,
	},

	/// A patch did not apply (context mismatch, missing file, malformed input).
	#[error("patch does not apply: {message}")]
	PatchFailed {
		/// Human-readable reason, including the offending path when known.
		message: String,
	},

	/// A CLI-backed operation (push/fetch/clone, reftable fallback) exited
	/// non-zero. Carries the captured streams for user-facing error surfaces.
	#[error("{}", crate::error::cli_message(command, *exit_code, stdout, stderr))]
	Cli {
		/// Rendered command line (`git push --no-follow-tags …`).
		command:   String,
		/// Process exit code.
		exit_code: i32,
		/// Captured stdout (may be truncated).
		stdout:    String,
		/// Captured stderr (may be truncated).
		stderr:    String,
	},

	/// A CLI-backed operation exceeded its deadline and was killed.
	#[error("timed out: {command}")]
	CliTimeout {
		/// Rendered command line.
		command: String,
	},

View on GitHub (pinned to 9690622007)

Solutions

  1. Read `stderr` in the error — git's own message usually names the exact problem (auth, rejected non-fast-forward, unknown option).
  2. For push rejections: `git pull --rebase` (fetch and integrate remote state), then push again.
  3. For auth failures: refresh credentials (`gh auth login`, ssh-add, credential manager) and retry.
  4. For unknown-option failures, upgrade the installed git/jj binary to a version supporting the flags used.

Example fix

// before
repo.push("origin", "main")?; // opaque failure
// after
if let Err(Error::Cli { command, exit_code, stderr, .. }) = repo.push("origin", "main") {
    eprintln!("{command} exited {exit_code}:\n{stderr}");
    if stderr.contains("non-fast-forward") { repo.pull_rebase("origin", "main")?; repo.push("origin", "main")?; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

fn remote_reachable(url: &str) -> bool {
    std::process::Command::new("git").args(["ls-remote", "--exit-code", url, "HEAD"])
        .status().map(|s| s.success()).unwrap_or(false)
}
if !remote_reachable(&remote_url) { eprintln!("cannot reach {remote_url} — check network/auth"); return Ok(()); }

Type guard

fn is_cli_error(err: &pi_vcs::Error) -> Option<(i32, &str)> {
    match err {
        pi_vcs::Error::Cli { exit_code, stderr, .. } => Some((*exit_code, stderr)),
        _ => None,
    }
}

Try / catch

match repo.push("origin", branch) {
    Err(pi_vcs::Error::Cli { exit_code, stderr, .. }) => {
        if stderr.contains("non-fast-forward") {
            repo.pull_rebase("origin", branch)?;
            repo.push("origin", branch)?;
        } else {
            anyhow::bail!("push failed (exit {exit_code}):\n{stderr}");
        }
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any CLI-spawned git/jj command failing: `git push` rejected (non-fast-forward, no upstream, auth failure), `git fetch` network/auth errors, `git clone` of a nonexistent URL, or the reftable fallback path failing against an old git binary.

Common situations: Expired/missing credentials (HTTPS token revoked, SSH agent not loaded), branch protected on the remote, remote behind local history (need pull/rebase), offline/VPN-down, or git too old to support a flag the library passes.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/06e7567734bf58f0. Report an issue: GitHub.