{"record":{"id":"290e50c778721d9c","repo":"can1357/oh-my-pi","slug":"timeout-reading-origin-url","errorCode":null,"errorMessage":"timeout reading origin url","messagePattern":"timeout reading origin url","errorType":"http","errorClass":"HTTPException","httpStatus":504,"severity":"error","filePath":"python/robomp/src/proxy/server.py","lineNumber":292,"sourceCode":"\n\ndef _read_remote_urls(repo_dir: Path, slot_uid: int | None = None, *, push: bool = False) -> list[str]:\n    \"\"\"Read every configured fetch URL or push URL for `origin` without contacting it.\"\"\"\n    env = _git_probe_env(repo_dir)\n    slot_kwargs = _slot_subprocess_kwargs(slot_uid)\n    selector = [\"--push\", \"--all\"] if push else [\"--all\"]\n    try:\n        proc = subprocess.run(\n            [\"git\", \"-C\", str(repo_dir), \"remote\", \"get-url\", *selector, \"origin\"],\n            capture_output=True,\n            text=True,\n            check=False,\n            timeout=_ORIGIN_READ_TIMEOUT_SECONDS,\n            env=env,\n            **slot_kwargs,\n        )\n    except subprocess.TimeoutExpired as exc:\n        raise HTTPException(504, \"timeout reading origin url\") from exc\n    if proc.returncode != 0:\n        # `git remote get-url` writes nothing useful to stdout on failure; do\n        # NOT echo stderr to the client (may leak local paths). The proxy log\n        # already captured the failure.\n        log.warning(\"gh-proxy: failed to read origin url\", extra={\"repo_dir\": str(repo_dir)})\n        raise HTTPException(400, \"could not read origin url for worktree\")\n    return [line.strip() for line in proc.stdout.splitlines() if line.strip()]\n\n\ndef _read_single_remote_url(repo_dir: Path, expected_repo: str, *, push: bool, slot_uid: int | None = None) -> str:\n    urls = list(dict.fromkeys(_read_remote_urls(repo_dir, slot_uid=slot_uid, push=push)))\n    if len(urls) != 1:\n        kind = \"push\" if push else \"fetch\"\n        log.warning(\n            \"gh-proxy: refusing git op — origin has ambiguous remote urls\",\n            extra={\"expected_repo\": expected_repo, \"kind\": kind, \"count\": len(urls)},\n        )\n        raise HTTPException(400, f\"origin must have exactly one {kind} url\")","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy/server.py#L274-L310","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (server env allows git to prompt, causing hangs)\nenv = {\"PATH\": os.environ[\"PATH\"]}\n// after (block interactive prompts so git fails fast instead of hanging)\nenv = {\"PATH\": os.environ[\"PATH\"], \"GIT_TERMINAL_PROMPT\": \"0\", \"GIT_ASKPASS\": \"/bin/true\"}","handlingStrategy":"retry","validationCode":"import subprocess\n\ndef origin_lookup_is_fast(repo_dir: str, timeout_s: float = 5.0) -> bool:\n    try:\n        subprocess.run([\"git\", \"-C\", repo_dir, \"remote\", \"get-url\", \"origin\"],\n                       timeout=timeout_s, capture_output=True, check=False)\n        return True\n    except subprocess.TimeoutExpired:\n        return False","typeGuard":null,"tryCatchPattern":"import httpx\nfrom fastapi import HTTPException\n\nfor attempt in range(3):\n    try:\n        return client.post(\"/git/op\", json={\"repo\": \"acme/repo\"})\n    except HTTPException as e:\n        if e.status_code != 504 or attempt == 2:\n            raise\n","preventionTips":["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"],"tags":["git","timeout","subprocess","proxy"],"backgroundTag":"git-command-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}