fix(czdev): login 失败时给出真实原因,而不是 JSONDecodeError - #18
Merged
Conversation
…deError `czdev login` polled the token endpoint and fed the body straight to json.loads() with no error handling, so any non-JSON reply surfaced as a bare `JSONDecodeError: Expecting value: line 1 column 1 (char 0)` traceback that hid the actual cause. These endpoints are on github.com, not api.github.com, so a rate-limit page, a proxy/captive-portal interception, or a redirect to an HTML page all arrive as HTTP 200 with HTML — and urllib follows redirects silently, so the landing URL was invisible too. post_form() now decodes the reply defensively and raises an AuthError naming the status, content-type, post-redirect URL and a body snippet. It also reads the body of 4xx responses (GitHub returns JSON errors there) and accepts the endpoint's default form-urlencoded encoding, which is what we'd get if anything stripped the Accept header. The poll loop retries transient failures instead of aborting a login the user may already be authorizing, prints the diagnostic on the first failure, and gives up after MAX_SOFT_FAILURES with a hint. Also: honor expires_in so it can't spin forever, grow the interval on slow_down as the spec requires, handle access_denied, add request timeouts, send a real User-Agent (github.com is more likely to throttle the default Python-urllib one), and turn Ctrl-C into "Cancelled." rather than a KeyboardInterrupt traceback. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
现象
同事跑
czdev login,拿到验证码后崩在轮询上:根因说明
这个 PR 不声称修掉了他那次的根因——根因目前未知。 从 traceback 只能确定两件事:
urlopen没抛HTTPError,所以 HTTP 状态是 2xx;响应体第 0 个字符不是合法 JSON 开头,即 body 为空或是 HTML/纯文本。已复现正常路径:未授权时 GitHub 返回
200+application/json+authorization_pending,协议用法本身是对的。可以排除「中间设备剥掉
Accept头导致 GitHub 回落 form-urlencoded」:几秒前同机同 host 同请求头的/login/device/code解析成功了。剩下的怀疑是 github.com 的限流页/代理拦截页/重定向到 HTML 页(这些端点在
github.com而非api.github.com,限流返回 HTML;urllib 还会静默跟随重定向)。但这是推测。所以这个 PR 修的是真正确定存在的 bug:轮询处零错误处理,把真实原因彻底吞掉了。修完之后他重跑一次,报错里就会直接写明状态码、content-type、最终 URL 和 body 片段。
改动
post_form()统一处理两个 OAuth 端点:解析失败时抛AuthError,带上 状态码 / content-type / 重定向后的最终 URL / body 片段;顺带读取 4xx 的 body(GitHub 在那里也返回 JSON 错误);并接受 form-urlencoded 作为回退(万一 Accept 头真被剥,登录会直接成功而不是报错)。MAX_SOFT_FAILURES=5次才放弃并给出限流/代理提示。expires_in不再可能无限循环;slow_down按规范永久增大 interval(原来只 sleep 5 秒,会一直被 slow_down);处理access_denied;加请求超时;补User-Agent(原来发的是默认Python-urllib/3.10,github.com 的 web 端点对这种 UA 更容易触发防护);Ctrl-C 输出Cancelled.并 exit 130,不再吐 KeyboardInterrupt traceback。验证
用本地假服务器覆盖了全部响应形态:HTML 页 / 空 body / form-urlencoded / 4xx+JSON / 纯文本 / 正常 token,以及轮询的重试自愈、重试耗尽放弃、
slow_down、access_denied。真实跑了一次czdev login:正常拿到 device code、静默轮询、Ctrl-C 干净退出 130 无 traceback。Made with Cursor