gastownhall/beads · error

oauth: failed to parse token response: %w

Error message

oauth: failed to parse token response: %w

What it means

The token endpoint returned HTTP 200 but the body could not be parsed as the expected token JSON (oauthTokenResponse). The server responded with something other than a well-formed access-token payload.

Source

Thrown at internal/linear/oauth.go:150

	}
	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")
	}

	m.token = tokenResp.AccessToken
	m.expiresAt = m.nowFunc().Add(time.Duration(tokenResp.ExpiresIn) * time.Second)

	debug.Logf("oauth: acquired token (expires in %ds)", tokenResp.ExpiresIn)
	return nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Dump (redacted) response body to confirm what the endpoint actually returned.
  2. Correct the TokenURL if it resolves to an HTML page with 200.
  3. Confirm the request's Accept/Content-Type headers are application/x-www-form-urlencoded as set by the client.
  4. Check whether a corporate proxy is rewriting the response.
Defensive patterns

Strategy: try-catch

Try / catch

if strings.Contains(err.Error(), "failed to parse token response") {
    // log/redact raw body and verify TokenURL returns JSON
}

Prevention

When it happens

Trigger: json.Unmarshal(body, &tokenResp) fails in acquireToken after a 200 response — e.g. body is HTML, empty, or a different JSON shape.

Common situations: TokenURL pointing at a page that returns 200 HTML; a proxy injecting content; provider changed response format (unlikely); content-type negotiation returning form-encoded data.

Understand the failure class

Related errors


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