gastownhall/beads · error

oauth: token response missing access_token

Error message

oauth: token response missing access_token

What it means

The token response parsed successfully as JSON but contained an empty access_token field. The server answered 200 with a syntactically valid but semantically empty token payload, so no usable credential was obtained.

Source

Thrown at internal/linear/oauth.go:154

	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. Verify grant_type and credentials produce a real grant at the provider (test with curl).
  2. Check for provider responses that embed errors with 200 and handle the error fields explicitly.
  3. Point the client at the real token endpoint instead of a stub/mock in the deployed environment.
  4. Update the client if the provider changed the token response schema.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: request a token and check the field yourself
var probe struct{ AccessToken string `json:"access_token"` }
_ = json.Unmarshal(body, &probe)
if probe.AccessToken == "" { /* fix grant/credentials before running */ }

Type guard

func hasAccessToken(r map[string]any) bool {
    t, ok := r["access_token"].(string)
    return ok && t != ""
}

Prevention

When it happens

Trigger: tokenResp.AccessToken == "" after successful json.Unmarshal in acquireToken — e.g. the server returned {} or only error/description fields with status 200.

Common situations: Provider quirk where errors are returned with 200; grant_type not actually supported so the payload lacks a token; a mock/stub server or misconfigured test double in the path.

Related errors


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