Skip to content

fix(agent): retry upstream clones so a GitHub 429 stops failing the image build - #67

Merged
venkateshsakamuri-lab merged 3 commits into
mainfrom
fix/agent-clone-retry-429
Aug 19, 2026
Merged

fix(agent): retry upstream clones so a GitHub 429 stops failing the image build#67
venkateshsakamuri-lab merged 3 commits into
mainfrom
fix/agent-clone-retry-429

Conversation

@geekypunk

Copy link
Copy Markdown
Contributor

Symptom

docker compose build has been failing intermittently on unrelated PRs — most recently twice today on cursor/admin-profile-switch-c497. Every other job passes; only the agent image fails.

Root cause

Not a code defect. Both upstream clones in agent/Dockerfile are unauthenticated requests to github.com, issued once, with no retry:

Cloning into 'runtime'...
remote: This request was rate-limited due to too many requests.
fatal: unable to access 'https://github.com/NousResearch/hermes-agent.git/':
       The requested URL returned error: 429
exit code: 128

GitHub rate-limits unauthenticated traffic per source IP, and Actions runners share pooled egress addresses. Under load the clone is refused and the whole image build dies with it. Nothing about the PR under test is involved — which is exactly why it looked random and kept landing on innocent branches.

Failure rate: 2 of the last 15 docker compose build runs. That flakiness pattern is itself the diagnosis; a real build break would fail 15 of 15.

Fix

Both clones retry up to 5 times with escalating backoff (15/30/45/60s). Retry is the right layer because the condition is transient — but the loop still prints FATAL and exits 1 on the last attempt, so an upstream that is genuinely gone fails the build loudly rather than yielding an image with no runtime in it.

This is not CI-only defensiveness: scripts/self-host/install.sh runs the same build, so a self-host user behind a shared NAT hits the identical refusal.

Also noted in a comment: the webui clone's existing || git clone fallback only ever handled a moved ref. Against a rate-limit it just re-issued the same refused request, so it needed the outer retry to be of any use at all.

Verification

  • Both edited RUN blocks pass sh -n as the shell actually receives them (continuations joined, in-RUN comments stripped). Baseline check caught a bug in my own extractor first — all 6 blocks including 3 untouched ones flagged, which is how I knew the harness was wrong rather than the Dockerfile.
  • Success path: the wrapper performs a real git clone of hermes-agent — exit 0, 76 entries, pyproject.toml present.
  • Give-up path: against an unreachable repo it retries 5×, prints FATAL, exits 1 (not 0).
  • CI docker compose build green on this PR — the authoritative test.

Follow-up, deliberately not bundled here

AGENT_RUNTIME_REF=main and AGENT_API_REF=master are unpinned branches, which contradicts the pinning rule in CLAUDE.md ("PINNED for reproducibility ... Bump deliberately and re-validate"). That's a second, independent failure mode — an upstream commit can break this build with no change on our side, which is precisely the class of bug the mcp>=1.0,<2 pin two lines below exists to prevent. Current upstream tags are v2026.8.18 (hermes-agent) and v0.52.76 (hermes-webui). Worth a separate PR that pins and re-validates.

🤖 Generated with Claude Code

…mage build

`docker compose build` failed on 2 of the last 15 CI runs, always in the
deepsql-agent image, always the same way:

    Cloning into 'runtime'...
    remote: This request was rate-limited due to too many requests.
    fatal: unable to access 'https://github.com/NousResearch/hermes-agent.git/':
           The requested URL returned error: 429
    exit code: 128

Both clones in agent/Dockerfile are unauthenticated requests to github.com,
issued once with no retry. GitHub rate-limits unauthenticated traffic per
source IP, and Actions runners share pooled egress addresses, so under load
the clone is refused and the whole image build dies with it. Nothing about the
pull request under test is involved, which is why the failure looked random
and landed on unrelated branches.

Both clones now retry up to five times with escalating backoff (15/30/45/60s).
The condition is genuinely transient, so retry is the correct layer rather
than a mask: on the final attempt the loop still prints FATAL and exits 1, so
an upstream that is actually gone fails the build loudly instead of producing
an image with no runtime in it.

This is not CI-only defensiveness. `scripts/self-host/install.sh` runs the
same build, so a self-host user behind a shared NAT hits the identical refusal.

Note the webui clone's existing `||` fallback only ever handled a *moved ref*;
against a rate-limit it re-issued the same refused request, so it needed the
outer retry to be of any use.

Verified: both RUN blocks pass `sh -n` as the shell receives them; the wrapper
clones hermes-agent for real (exit 0), and against an unreachable repo it
retries 5x and exits 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@geekypunk
geekypunk requested a review from a team as a code owner August 19, 2026 00:55
The fix in this branch is only half the value; the diagnosis is the other
half. An intermittent CI failure landing on branches whose diff has nothing
to do with the agent reads as flaky infrastructure, and the instinct is to
re-run or bisect. It was neither — it was a per-IP rate limit on an
unauthenticated clone, visible in one line of the build log.

Filed under Agent Runtime Rules rather than Verification Anti-Patterns: it is
a property of how the agent image is assembled, next to the MCP SDK pin that
guards the same build against the same class of upstream surprise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
venkateshsakamuri-lab pushed a commit that referenced this pull request Aug 19, 2026
Follow-up to #67, which fixed the *transient* half of the agent build's
fragility. This fixes the *silent* half.

## Problem

Both install paths tracked **moving branches**:

| path | before | behaviour |
|---|---|---|
| `agent/Dockerfile` | `AGENT_RUNTIME_REF=main`, `AGENT_API_REF=master`
| whatever the branch points at today |
| `scripts/self-host/setup-agent.sh` | `git clone --depth 1 "$repo"` —
**no ref at all** | default-branch HEAD |

An upstream commit can therefore change what the agent image contains,
and break it, **with no change on our side**. That is precisely the
failure mode the `mcp>=1.0,<2` pin four lines below in the same file
exists to prevent — left wide open one layer up. `CLAUDE.md` already
states the rule (*"PINNED for reproducibility ... Bump deliberately and
re-validate"*); this repo just wasn't following it.

## Change

Pinned to the current release pair:

```
hermes-agent  v2026.8.18  ->  e624e9f
hermes-webui  v0.52.76    ->  3c9304a
```

**This is a behaviour change, not a no-op.** Both branches had already
moved past their latest tag — `main` was at `1f234a1`, `master` at
`63a562f` — so this rolls the runtime back from branch HEAD to the
tagged release. Reviewers should weigh that deliberately rather than
read "pinning" as cosmetic.

**Both files change together on purpose.** Pinning only the Dockerfile
would leave host installs on branch HEAD and container installs on a
tag, so a bug would reproduce on one path and not the other. The
Dockerfile comment already asserted the two tracked each other — that
assertion was false, and this makes it true.

`ensure_clone` still returns early when a checkout already exists, so an
install predating this keeps its current ref. Silently deleting a user's
agent directory to change a version isn't this script's call — but the
message now says the ref was left alone instead of implying it was
applied.

## Verification

- [x] Both tags clone at the **exact expected SHAs**, with
`pyproject.toml` and `requirements.txt` present
- [x] `setup-agent.sh` passes `bash -n`
- [ ] CI `docker compose build` green — proves the pinned refs *build*
- [ ] **Agent smoke test — not covered by CI, and required before
merge**

That last box is the important one. Per `CLAUDE.md`'s own anti-patterns,
*"a dashboard that is HTML and long proves nothing"* and presence ≠
compatibility: a green `docker compose build` proves these refs compile
and install, **not** that the agent answers. Since this moves the
runtime to a different commit than what's been running, someone should
confirm the Agent tab returns a real answer (not "I'm blocked") against
a pinned image before this lands. I can't run that from here.

## Ordering note

Independent of #67 — different lines of `agent/Dockerfile`, no conflict
expected — but #67 should land first so the retry protects these clones
too.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@venkateshsakamuri-lab

Copy link
Copy Markdown
Contributor

@geekypunk update the branch from main and merge it.

@venkateshsakamuri-lab
venkateshsakamuri-lab merged commit e7834df into main Aug 19, 2026
9 checks passed
@venkateshsakamuri-lab
venkateshsakamuri-lab deleted the fix/agent-clone-retry-429 branch August 19, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants