gastownhall/beads · error

oauth: failed to read token response: %w

Error message

oauth: failed to read token response: %w

What it means

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.

Source

Thrown at internal/linear/oauth.go:137

		"scope":         {m.config.Scopes},
		"actor":         {m.config.Actor},
	}

	req, err := http.NewRequest("POST", m.config.TokenURL, strings.NewReader(data.Encode()))
	if err != nil {
		return fmt.Errorf("oauth: failed to create token request: %w", err)
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	resp, err := m.client.Do(req)
	if err != nil {
		return fmt.Errorf("oauth: token request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1MB limit
	if err != nil {
		return fmt.Errorf("oauth: failed to read token response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		var errResp oauthErrorResponse
		if json.Unmarshal(body, &errResp) == nil && errResp.Error != "" {
			return fmt.Errorf("oauth: token request failed (%s): %s", errResp.Error, errResp.Description)
		}
		return fmt.Errorf("oauth: token request returned status %d: %s", resp.StatusCode, string(body))
	}

	var tokenResp oauthTokenResponse
	if err := json.Unmarshal(body, &tokenResp); err != nil {
		return fmt.Errorf("oauth: failed to parse token response: %w", err)
	}

	if tokenResp.AccessToken == "" {
		return fmt.Errorf("oauth: token response missing access_token")
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the token request once with backoff; this failure is often transient.
  2. Check intermediaries (LB, proxy) for connection-reset logs around the time of failure.
  3. Ensure the token endpoint supports HTTP keep-alive correctly or test with HTTP/1.1 vs HTTP/2 toggles.
Defensive patterns

Strategy: retry

Try / catch

if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.Canceled) {
    // transient/truncated body — retry once with backoff
}

Prevention

When it happens

Trigger: io.ReadAll(io.LimitReader(resp.Body, 1<<20)) errors inside acquireToken after a successful HTTP response status line.

Common situations: Server or an intermediary closed the connection before the full body arrived; flaky mobile/VPN links; load balancer prematurely terminating chunked responses.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d79e3110cea34f0f. Report an issue: GitHub.