{"record":{"id":"cd2f79b134cb3dd3","repo":"gastownhall/beads","slug":"oauth-failed-to-create-token-request-w","errorCode":null,"errorMessage":"oauth: failed to create token request: %w","messagePattern":"oauth: failed to create token request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/oauth.go","lineNumber":125,"sourceCode":"\tdefer m.mu.Unlock()\n\tm.token = \"\"\n\tm.expiresAt = time.Time{}\n\tdebug.Logf(\"oauth: token invalidated, will re-acquire on next request\")\n}\n\n// acquireToken performs the client_credentials grant. Caller must hold m.mu write lock.\nfunc (m *OAuthTokenManager) acquireToken() error {\n\tdata := url.Values{\n\t\t\"grant_type\":    {\"client_credentials\"},\n\t\t\"client_id\":     {m.config.ClientID},\n\t\t\"client_secret\": {m.config.ClientSecret},\n\t\t\"scope\":         {m.config.Scopes},\n\t\t\"actor\":         {m.config.Actor},\n\t}\n\n\treq, err := http.NewRequest(\"POST\", m.config.TokenURL, strings.NewReader(data.Encode()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to create token request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\tresp, err := m.client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: token request failed: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1MB limit\n\tif err != nil {\n\t\treturn fmt.Errorf(\"oauth: failed to read token response: %w\", err)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tvar errResp oauthErrorResponse\n\t\tif json.Unmarshal(body, &errResp) == nil && errResp.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"oauth: token request failed (%s): %s\", errResp.Error, errResp.Description)","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/oauth.go#L107-L143","documentation":"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.","triggerScenarios":"acquireToken (called via Token) builds a POST to m.config.TokenURL; http.NewRequest returns an error for an unparsable URL or invalid method.","commonSituations":"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.","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."],"exampleFix":"// before\nTokenURL: os.Getenv(\"LINEAR_TOKEN_URL\")\n// after\ntokenURL := strings.TrimSpace(os.Getenv(\"LINEAR_TOKEN_URL\"))\nif u, err := url.Parse(tokenURL); err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    return nil, fmt.Errorf(\"invalid LINEAR_TOKEN_URL %q: %w\", tokenURL, err)\n}","handlingStrategy":"validation","validationCode":"u, err := url.Parse(cfg.TokenURL)\nif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n    return fmt.Errorf(\"invalid token URL %q\", cfg.TokenURL)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["oauth","http-request","configuration","go"],"backgroundTag":"invalid-request-url","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}