Repository navigation
Conversation
GitHub's device-flow token endpoint returns slow_down when the client polls too frequently. Per RFC 8628 §3.5 this is a non-terminal signal: the client must increase its polling interval by 5 seconds and keep polling. The current pollForToken implementation only handles authorization_pending as retriable and bails on every other error, turning a routine rate-limit response into an unrecoverable "login failed: error polling for token: token request failed: slow_down". Treat slow_down the same as authorization_pending, bumping the interval per the RFC. Fixes modelcontextprotocol#1289
This comment was marked as spam.
This comment was marked as spam.
|
@P4ST4S good suggestions, let me check and test both these cases. |
Address review feedback on the device-flow slow_down fix:
- Add regression tests for pollForToken (previously untested):
- slow_down then success: confirms polling continues to success
- slow_down / authorization_pending / slow_down then success: confirms
the RFC 8628 §3.5 interval increment compounds across mixed retryable
errors and is not reset between slow_down instances
- repeated slow_down: confirms the interval is capped
- non-retryable error: confirms polling stops immediately
- Cap the polling interval at 60s so a misbehaving auth server returning
slow_down repeatedly cannot grow it unboundedly (RFC 8628 §3.5 mandates
the +5s increase but leaves the maximum to the implementation).
To make pollForToken testable, the access-token URL, initial interval, and
sleep function are now provider fields wired to defaults in the constructor;
tests inject a mock server and capture the back-off sequence deterministically.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks for the thorough review, @P4ST4S! Both points are addressed in 9c5e9fc. 1. Regression tests —
2. Interval cap — added a 60s ceiling so a misbehaving auth server returning
|
This comment was marked as spam.
This comment was marked as spam.
JosephDoUrden
left a comment
There was a problem hiding this comment.
Tested this locally. go test ./cmd/publisher/... all green, the new pollForToken tests pass with -race too. slow_down handling follows RFC 8628, +5s each time and it sticks, 60s cap also works. Tests are solid btw, I tried to break the code on purpose (reset the interval, remove the cap) and they caught both. Branch is ~60 commits behind main but it merges clean, tests still pass after merge. Looks merge-ready to me.
Resolve overlapping GitHub device-flow changes after modelcontextprotocol#1290 merged, preserving the device-code grace retry implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…polling (#1588) Follow-up to my analysis on #1543. Heads up on the shape first: this builds on #1290's poll-loop seams and github wont let me target a fork branch, so the diff shows its two commits too. Review only the last commit, the first two are #1290 unchanged, credit to @developer-ishan. Happy to rebase whichever way #1290 goes, or split differently if that's easier to review. What it does. pollForToken treats every non-pending answer as terminal, so one flaky response kills a login the user may already have approved in the browser. This spends a small grace budget (2 polls) on incorrect_device_code before giving up, retries 5xx with a growing backoff, and returns status plus body on other non-200s instead of dying inside json.Unmarshal on an html error page. Terminal errors now carry error_description and error_uri, and a timeout that happens after polling recovered reports a plain timeout, not a stale error from minutes earlier. Also honours the interval and expires_in GitHub returns with the device code (RFC 8628 3.2) instead of hardcoding 5/900, clamped (60s / 3600s) so a misbehaving response can't stall the CLI or overflow the duration math. One thing I changed my mind on from the issue comment: I wrote there that the client should wait one interval before the first poll. Dropped that. My own repro test showed 40s of clean polling before authorize, so the propagation theory doesn't hold, and the grace retry covers the same window without taxing every fast login. First poll stays immediate, pinned by a test. Root cause note: I can't prove the GitHub-side race (cli/cli#9302 is the same error class but was closed on an unconfirmed "platform race"). So this isn't claimed as a root-cause fix, it makes the login recoverable if the condition is transient and diagnosable if it isn't. Btw incorrect_device_code is a GitHub extension, not an RFC 8628 3.5 code, so there's no spec retry semantic being violated by the grace polls. ## Test 12 new tests in github_at_device_flow_internal_test.go, all green with -race (33 in the package). #1290's four tests pass unmodified. golangci-lint clean in the touched files under both v2.11.4 (this branch's CI pin) and v2.13.1 (what main pins now). ## AI Disclosure AI assistance (Claude) was used for issue research and during development. All changes were reviewed and tested by the author. Fixes #1543 --------- Co-authored-by: developer-ishan <igupta@groupon.com> Co-authored-by: Ishan Gupta <62565307+developer-ishan@users.noreply.github.com> Co-authored-by: developer-ishan <guptaishan849@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
slow_downresponse per RFC 8628 §3.5 — treat it as retriable and bump the polling interval by 5 seconds, instead of bailing with a fatal error.Fixes #1289.
Why
pollForTokenincmd/publisher/auth/github-at.goonly treatsauthorization_pendingas retriable. Every other response from GitHub's token endpoint — including the non-terminalslow_downsignal — falls through to:So users hitting GitHub's polling rate-limit get:
…and have to re-run
mcp-publisher login githubfrom scratch, which is especially painful becauseslow_downis more likely when the user takes more than a few seconds to enter and authorize the device code.Per RFC 8628 §3.5,
slow_downis defined as:Change
Treat
slow_downthe same asauthorization_pendingbut increaseintervalby 5 seconds before sleeping, matching the RFC exactly.A possible follow-up (not in this PR, to keep the diff minimal): seed the initial
intervalfromDeviceCodeResponse.Interval(already parsed but currently unused) instead of hardcoding5. Happy to do that here or as a separate PR if preferred.Test plan
go build ./cmd/publisher/...— cleango test ./cmd/publisher/auth/...— passesgofmt -dandgo vet ./cmd/publisher/auth/...— cleanmake check(lint + integration tests)I could not exercise
pollForTokenfrom a unit test without refactoringGitHubAccessTokenURLto be injectable, which felt out of scope for a bug-fix PR. Glad to add that refactor + a table-driven test forauthorization_pending/slow_down/ fatal errors in a follow-up if maintainers want it.