can1357/oh-my-pi · error · GitCommandError

git timed out after {effective_timeout:.0f}s: {' '.join(_red

Error message

git timed out after {effective_timeout:.0f}s: {' '.join(_redacted_cmd(cmd))}

What it means

`_run_git` wraps `subprocess.TimeoutExpired` into a `GitCommandError` with exit code 124 (mirroring GNU timeout) and a message stating how long git was allowed to run plus the credential-redacted command. The library raises this so callers only need to handle one git-failure type instead of also special-casing timeouts; the direct child is already killed when the timeout fires.

Source

Thrown at python/robomp/src/git_ops.py:277

    try:
        proc = subprocess.run(
            cmd,
            cwd=str(cwd) if cwd else None,
            env=env,
            check=False,
            capture_output=True,
            text=True,
            timeout=effective_timeout,
            **subprocess_kwargs,
        )
    except subprocess.TimeoutExpired as exc:
        # `subprocess.run` already kills the direct child when the timeout
        # fires, but we explicitly re-raise as `GitCommandError` so callers
        # don't have to special-case `TimeoutExpired` alongside the regular
        # non-zero-exit error path. 124 mirrors GNU `timeout`.
        stdout = redact_credentials(exc.stdout or "") if isinstance(exc.stdout, str) else ""
        stderr_msg = f"git timed out after {effective_timeout:.0f}s: {' '.join(_redacted_cmd(cmd))}"
        raise GitCommandError(cmd, 124, stdout, stderr_msg) from exc
    if proc.stdout:
        proc.stdout = redact_credentials(proc.stdout)
    if proc.stderr:
        proc.stderr = redact_credentials(proc.stderr)
    return proc


def _check(proc: subprocess.CompletedProcess[str], cmd: list[str]) -> subprocess.CompletedProcess[str]:
    if proc.returncode != 0:
        raise GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)
    return proc


def _git_dir(repo_dir: Path) -> Path | None:
    dot_git = repo_dir / ".git"
    if dot_git.is_dir():
        return dot_git
    if dot_git.is_file():

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the command — timeouts are often transient network issues
  2. Increase the configured timeout (e.g. ROBOMP_GH_PROXY_GIT_TIMEOUT_SECONDS) if the repo legitimately needs longer
  3. Ensure git never blocks on prompts: set GIT_TERMINAL_PROMPT=0 and configure a credential helper or token in the URL
  4. Check connectivity to the remote / proxy before retrying

Example fix

// before
_run_git(["clone", url, dest])  # default timeout too short for big repo
// after
_run_git(["clone", url, dest], timeout=600)  # and export GIT_TERMINAL_PROMPT=0
Defensive patterns

Strategy: retry

Validate before calling

import shutil, os
if not shutil.which("git"): raise RuntimeError("git not installed")
os.environ.setdefault("GIT_TERMINAL_PROMPT", "0")  # prevent hangs on auth prompts

Try / catch

from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
@retry(retry=retry_if_exception_type(GitCommandError), stop=stop_after_attempt(3), wait=wait_exponential(min=2))
def do_clone(url, dest):
    clone(url, dest)

Prevention

When it happens

Trigger: Any git operation routed through `_run_git` (clone, fetch_prune, fetch_ref, _worktrees_holding_refs, _remove_worktrees, _delete_bad_refs) exceeding `effective_timeout` — typically a slow/blocked network fetch, a huge clone, or git hanging on a credential prompt.

Common situations: Flaky or throttled network to the remote, git waiting on an interactive username/password prompt in a non-interactive environment (no GIT_TERMINAL_PROMPT=0), proxy transport stalls, very large repos on slow disks.

Understand the failure class

Related errors


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