container-tests in ci.yml runs docker build -t mcp-debugger:local . with no registry login, so the base-image manifests come from Docker Hub under the anonymous rate limit shared by every GitHub-hosted runner on that egress. Twice on one docs-only PR today (#888, run 37990703352, 2026-10-09):
#7 ERROR: failed to copy: httpReadSeeker: failed open: unexpected status code
https://registry-1.docker.io/v2/library/node/manifests/sha256:deae974a…: 429 Too Many Requests (21:01 UTC)
#9 ERROR: ... https://registry-1.docker.io/v2/library/dart/manifests/sha256:8604cbd1…: 429 Too Many Requests (rerun, 21:12 UTC)
The same job passed on main at e44f1fe four hours earlier, so this is the limit, not the Dockerfile. Pinning by digest does not help: the manifest fetch itself is what gets counted. The PR merged because Container Tests is not a required check, which also means a 429 silently turns Test Summary red on main push runs.
Proposal:
- Log in before the build when the secrets are available: a step that sets an output from
DOCKER_USERNAME/DOCKER_PASSWORD (already used by release.yml), then docker/login-action guarded by that output. Fork and Dependabot PRs have no secrets and keep building anonymously, so nothing new fails for contributors; authenticated pulls lift the per-IP anonymous limit for everyone else. The release workflow's Docker job already logs in and has not seen this.
- Or mirror the two base images (
node:26-slim, dart:3.13, both digest-pinned) to GHCR under debugmcp and build from there; more moving parts, no Docker Hub dependency on PRs at all.
Either way a 429 should read as "retry", not as a container failure: the step could retry the build once after a pause when the output contains 429 Too Many Requests.
container-testsinci.ymlrunsdocker build -t mcp-debugger:local .with no registry login, so the base-image manifests come from Docker Hub under the anonymous rate limit shared by every GitHub-hosted runner on that egress. Twice on one docs-only PR today (#888, run 37990703352, 2026-10-09):The same job passed on
mainat e44f1fe four hours earlier, so this is the limit, not the Dockerfile. Pinning by digest does not help: the manifest fetch itself is what gets counted. The PR merged becauseContainer Testsis not a required check, which also means a 429 silently turnsTest Summaryred onmainpush runs.Proposal:
DOCKER_USERNAME/DOCKER_PASSWORD(already used byrelease.yml), thendocker/login-actionguarded by that output. Fork and Dependabot PRs have no secrets and keep building anonymously, so nothing new fails for contributors; authenticated pulls lift the per-IP anonymous limit for everyone else. The release workflow's Docker job already logs in and has not seen this.node:26-slim,dart:3.13, both digest-pinned) to GHCR underdebugmcpand build from there; more moving parts, no Docker Hub dependency on PRs at all.Either way a 429 should read as "retry", not as a container failure: the step could retry the build once after a pause when the output contains
429 Too Many Requests.