gastownhall/beads · error

oauth: token request failed: %w

Error message

oauth: token request failed: %w

What it means

The OAuth token HTTP request itself failed at the transport level (connection refused, DNS failure, TLS error, timeout). The authorization server was unreachable, so no token could be issued.

Source

Thrown at internal/linear/oauth.go:131

// acquireToken performs the client_credentials grant. Caller must hold m.mu write lock.
func (m *OAuthTokenManager) acquireToken() error {
	data := url.Values{
		"grant_type":    {"client_credentials"},
		"client_id":     {m.config.ClientID},
		"client_secret": {m.config.ClientSecret},
		"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 {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check network egress and proxy settings (HTTPS_PROXY) from the host running the code.
  2. Verify TokenURL hostname resolves and is reachable (curl -v the endpoint).
  3. Increase the HTTP client timeout if the failure is a timeout on a slow network.
  4. Configure the http.Client's TLS settings if a corporate MITM certificate is in play.

Example fix

// before
client: &http.Client{}
// after
client: &http.Client{
    Timeout: 15 * time.Second,
    Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
}
Defensive patterns

Strategy: retry

Try / catch

var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
    // retry with exponential backoff
}
// otherwise surface the wrapped oauth error to the operator

Prevention

When it happens

Trigger: m.client.Do(req) returns an error inside acquireToken during a client-credentials token exchange.

Common situations: Corporate proxy or firewall blocking the token endpoint; DNS outage; TLS cert issues (self-signed MITM proxies); TokenURL pointing at a wrong host or a dead internal endpoint.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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