{"record":{"id":"d79e3110cea34f0f","repo":"gastownhall/beads","slug":"oauth-failed-to-read-token-response-w","errorCode":null,"errorMessage":"oauth: failed to read token response: %w","messagePattern":"oauth: failed to read token response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/oauth.go","lineNumber":137,"sourceCode":"\t\t\"scope\":         {m.config.Scopes},\n\t\t\"actor\":         {m.config.Actor},\n\t}\n\n\treq, err := http.NewRequest(\"POST\", m.config.TokenURL, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to create token request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\tresp, err := m.client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: token request failed: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1MB limit\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to read token response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp oauthErrorResponse\n\t\tif json.Unmarshal(body, &errResp) == nil && errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"oauth: token request failed (%s): %s\", errResp.Error, errResp.Description)\n\t\t}\n\t\treturn fmt.Errorf(\"oauth: token request returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar tokenResp oauthTokenResponse\n\tif err := json.Unmarshal(body, &tokenResp); err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to parse token response: %w\", err)\n\t}\n\n\tif tokenResp.AccessToken == \"\" {\n\t\treturn fmt.Errorf(\"oauth: token response missing access_token\")\n\t}","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/oauth.go#L119-L155","documentation":"The token endpoint responded, but reading the response body failed. The body is read with a 1MB limit via io.LimitReader, so this is an underlying read/IO error (connection reset mid-response, truncated chunked encoding), not an oversized body.","triggerScenarios":"io.ReadAll(io.LimitReader(resp.Body, 1<<20)) errors inside acquireToken after a successful HTTP response status line.","commonSituations":"Server or an intermediary closed the connection before the full body arrived; flaky mobile/VPN links; load balancer prematurely terminating chunked responses.","solutions":["Retry the token request once with backoff; this failure is often transient.","Check intermediaries (LB, proxy) for connection-reset logs around the time of failure.","Ensure the token endpoint supports HTTP keep-alive correctly or test with HTTP/1.1 vs HTTP/2 toggles."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.Canceled) {\n    // transient/truncated body — retry once with backoff\n}","preventionTips":["Retry token acquisition; partial reads are usually transient.","Avoid reusing flaky keep-alive connections for auth traffic.","Check intermediary proxies for connection-reset issues."],"tags":["oauth","http-response","io","go"],"backgroundTag":"response-body-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}