Skip to content

Retry a request gh sent and got no answer to - #10

Merged
jeffwall-curlewlabs merged 1 commit into
mainfrom
fix/retry-when-gh-gets-no-answer
Oct 13, 2026
Merged

jeffwall-curlewlabs merged 1 commit into
mainfrom
fix/retry-when-gh-gets-no-answer

Conversation

@jeffwall-curlewlabs

Copy link
Copy Markdown
Contributor

gh_api retries a busy or unreachable API. For a failure with no HTTP status, though, the only form it retried was gh's error connecting to line, and gh prints that for a failed name lookup alone. Every other request that gets no answer prints Go's error for it, and that failed on the first attempt:

Get "https://api.github.com/repos/<repo>/git/matching-refs/github-cron-trigger-clock/?per_page=100": dial tcp 20.29.134.17:443: i/o timeout
Get "https://api.github.com/repos/<repo>/git/matching-refs/github-cron-trigger-clock/?per_page=100": net/http: TLS handshake timeout

Over two days, a clock host logged both of these on the mark read. A tick that cannot read the marks sends nothing and exits 1, so each one pushed a slot back to the next tick. The same host also logged a stale-mark delete failing this way. Pruning is housekeeping, so that one didn't fail its tick.

Go's error opens with the method and the quoted URL (Get "<url>": ). A status-less failure in that shape now counts as unanswered and goes through the existing retry ladder. I matched the shape rather than a list of causes, because Go words those many ways. A cause no retry clears, such as a refused certificate, costs only the ladder's waits before the same failure.

Retrying a request that may already have applied isn't new. gh_api already retried one after a timeout or a 5xx, and the callers cover it:

  • A mark claim reads back whose blob the mark holds.
  • A ledger create or delete takes its 422 as done.
  • A second dispatch of a slot is one more delivery, which the concurrency group and the slot action run once.

Tests:

  • The classifier now has to treat four no-answer lines as transient: the two logged lines, a DELETE timeout, and a GraphQL POST through a proxy that refused. All four fail against v0.2.1's code.
  • A gh usage error also has no status. It has to stay non-transient, so the match can't widen to "anything without a status".
  • The forms come from gh 2.101.0 itself. With HTTPS_PROXY at a closed port, it prints Go's error for GET, DELETE and GraphQL POST. With the proxy at a name that doesn't resolve, it prints error connecting to.

I ran these on a Mac: the unit tests on Python 3.14, ruff format --check, ruff check and mypy. I didn't run the live check locally; CI runs it.

The version goes to 0.2.2.

gh_api retries a busy or unreachable API, but the only status-less failure
it counted as unreachable was gh's "error connecting to" line, which gh
prints for a failed name lookup alone. Every other request left unanswered
- a dial or TLS handshake that timed out, a reset connection, a proxy that
refused - prints Go's error for it instead, such as

  Get "https://api.github.com/...": dial tcp 20.29.134.17:443: i/o timeout

and that failed on the first attempt. On a clock host it happened to the
mark read, so the tick sent nothing and exited 1, and its slot waited for
the next tick.

Go's error opens with the method and the quoted URL, so that shape is what
counts as unanswered now, rather than a list of causes Go words many ways.
A cause no retry clears, such as a refused certificate, costs only the
ladder's waits before the same failure. A request that applied before its
answer was lost was already retried on a timeout or a 5xx, and its callers
cover that: a mark claim reads back whose blob the mark holds, a ledger
create or delete takes its 422 as done, and a second dispatch of a slot is
one more delivery, which the concurrency group and slot action run once.

The version goes to 0.2.2.
@jeffwall-curlewlabs
jeffwall-curlewlabs merged commit bc069fa into main Oct 13, 2026
@jeffwall-curlewlabs
jeffwall-curlewlabs deleted the fix/retry-when-gh-gets-no-answer branch October 13, 2026 02:53
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.

1 participant