gastownhall/beads · error
oauth: failed to create token request: %w
Error message
oauth: failed to create token request: %w
What it means
Wraps http.NewRequest failures during OAuth token acquisition. The token request URL or body could not be constructed, so no HTTP call was attempted. Usually indicates a malformed TokenURL (bad scheme/control characters) rather than a transient issue.
Source
Thrown at internal/linear/oauth.go:125
defer m.mu.Unlock()
m.token = ""
m.expiresAt = time.Time{}
debug.Logf("oauth: token invalidated, will re-acquire on next request")
}
// 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)View on GitHub (pinned to 71377f2769)
Solutions
- Validate m.config.TokenURL at startup (url.Parse must succeed and scheme must be http/https).
- Log the TokenURL (redacted) from the wrapped error context to spot encoding artifacts.
- Trim whitespace/newlines from the token URL when loading config from env or files.
Example fix
// before
TokenURL: os.Getenv("LINEAR_TOKEN_URL")
// after
tokenURL := strings.TrimSpace(os.Getenv("LINEAR_TOKEN_URL"))
if u, err := url.Parse(tokenURL); err != nil || (u.Scheme != "http" && u.Scheme != "https") {
return nil, fmt.Errorf("invalid LINEAR_TOKEN_URL %q: %w", tokenURL, err)
} Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(cfg.TokenURL)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
return fmt.Errorf("invalid token URL %q", cfg.TokenURL)
} Prevention
- Validate TokenURL at config load time, not at request time.
- Trim whitespace from env-derived URLs.
- Store token endpoints as constants, not free-form strings.
When it happens
Trigger: acquireToken (called via Token) builds a POST to m.config.TokenURL; http.NewRequest returns an error for an unparsable URL or invalid method.
Common situations: TokenURL set to an empty string or a value with spaces/newlines from a misparsed env var; config built with url.QueryEscape applied twice; typo like 'htp://' in configuration.
Related errors
- dolt directory is required
- invalid database name: %q; hyphens are not allowed in embedd
- embeddeddolt: invalid database name: %q; hyphens are not all
- failed to open database: %w Hint: %s
- failed to load %s: %w; no storage database was opened or mod
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/cd2f79b134cb3dd3.
Report an issue: GitHub.