can1357/oh-my-pi · error · HTTPException
timeout reading origin url
Error message
timeout reading origin url
What it means
This 504 error is raised by the gh-proxy when spawning `git remote get-url origin` in a worktree takes longer than _ORIGIN_READ_TIMEOUT_SECONDS. The proxy deliberately wraps subprocess.TimeoutExpired into an HTTP 504 so the client knows the origin lookup timed out rather than failed. It indicates the local git command hung, typically because the repository lives on slow/locked storage or git is blocked on a credential/SSH prompt.
Source
Thrown at python/robomp/src/proxy/server.py:292
def _read_remote_urls(repo_dir: Path, slot_uid: int | None = None, *, push: bool = False) -> list[str]:
"""Read every configured fetch URL or push URL for `origin` without contacting it."""
env = _git_probe_env(repo_dir)
slot_kwargs = _slot_subprocess_kwargs(slot_uid)
selector = ["--push", "--all"] if push else ["--all"]
try:
proc = subprocess.run(
["git", "-C", str(repo_dir), "remote", "get-url", *selector, "origin"],
capture_output=True,
text=True,
check=False,
timeout=_ORIGIN_READ_TIMEOUT_SECONDS,
env=env,
**slot_kwargs,
)
except subprocess.TimeoutExpired as exc:
raise HTTPException(504, "timeout reading origin url") from exc
if proc.returncode != 0:
# `git remote get-url` writes nothing useful to stdout on failure; do
# NOT echo stderr to the client (may leak local paths). The proxy log
# already captured the failure.
log.warning("gh-proxy: failed to read origin url", extra={"repo_dir": str(repo_dir)})
raise HTTPException(400, "could not read origin url for worktree")
return [line.strip() for line in proc.stdout.splitlines() if line.strip()]
def _read_single_remote_url(repo_dir: Path, expected_repo: str, *, push: bool, slot_uid: int | None = None) -> str:
urls = list(dict.fromkeys(_read_remote_urls(repo_dir, slot_uid=slot_uid, push=push)))
if len(urls) != 1:
kind = "push" if push else "fetch"
log.warning(
"gh-proxy: refusing git op — origin has ambiguous remote urls",
extra={"expected_repo": expected_repo, "kind": kind, "count": len(urls)},
)
raise HTTPException(400, f"origin must have exactly one {kind} url")View on GitHub (pinned to 9690622007)
Solutions
- Retry the request — transient stalls (mount latency, load) often resolve; keep retries bounded
- Check that the repo_dir path is on fast local storage and not a stale/hung network mount
- Disable interactive git prompts for the proxy env (GIT_TERMINAL_PROMPT=0, GIT_ASKPASS=echo) so git cannot block on credentials
- Inspect `git -C <repo_dir> remote get-url origin` manually to reproduce and time the hang
- If hangs are chronic, run the git lookup out-of-band (cache remote urls) instead of per-request
Example fix
// before (server env allows git to prompt, causing hangs)
env = {"PATH": os.environ["PATH"]}
// after (block interactive prompts so git fails fast instead of hanging)
env = {"PATH": os.environ["PATH"], "GIT_TERMINAL_PROMPT": "0", "GIT_ASKPASS": "/bin/true"} Defensive patterns
Strategy: retry
Validate before calling
import subprocess
def origin_lookup_is_fast(repo_dir: str, timeout_s: float = 5.0) -> bool:
try:
subprocess.run(["git", "-C", repo_dir, "remote", "get-url", "origin"],
timeout=timeout_s, capture_output=True, check=False)
return True
except subprocess.TimeoutExpired:
return False Try / catch
import httpx
from fastapi import HTTPException
for attempt in range(3):
try:
return client.post("/git/op", json={"repo": "acme/repo"})
except HTTPException as e:
if e.status_code != 504 or attempt == 2:
raise
Prevention
- Set GIT_TERMINAL_PROMPT=0 and a non-interactive askpass so git never blocks on prompts
- Keep worktrees on local, not network, storage
- Avoid git config (hooks, insteadOf, aliases) that shells out to slow commands in proxy-managed repos
- Cache remote URLs instead of shelling out per request
When it happens
Trigger: Calling a proxy git endpoint whose handler calls _read_single_remote_url → _read_remote_urls when `git remote get-url origin` (optionally with push or a slot uid via slot_kwargs) does not exit within _ORIGIN_READ_TIMEOUT_SECONDS and subprocess raises TimeoutExpired.
Common situations: NFS/lazy-mounted repo directories stalling git; a git credential helper blocking on an interactive prompt; core.hooksPath or insteadOf config invoking something slow; heavy system load or a wedged filesystem; an alias/wrapper intercepting git.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- git timed out after {effective_timeout:.0f}s: {' '.join(_red
- 124
- timed out: {command}
- Timed out waiting for RPC ready signal. Stderr: {stderr}
- GitCommandError(cmd, proc.returncode, proc.stdout, proc.stde
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/290e50c778721d9c.
Report an issue: GitHub.