wtfutil/wtf · error
Couldn't get token
Error message
Couldn't get token
What it means
The spotifyweb module's OAuth2 callback handler (authHandler) calls auth.Token(state, r) to exchange the authorization response for a token during the Spotify authentication flow. If the token exchange fails, it responds with HTTP 403 "Couldn't get token" via http.Error. Note the handler continues execution after http.Error (no return), so auth.NewClient is still called with a possibly-nil token, which can itself cause a subsequent nil-pointer panic.
Source
Thrown at modules/spotifyweb/widget.go:49
Status string
}
// Widget is the struct used by all WTF widgets to transfer to the main widget controller
type Widget struct {
view.TextWidget
Info
client *spotify.Client
clientChan chan *spotify.Client
playerState *spotify.PlayerState
settings *Settings
}
func authHandler(w http.ResponseWriter, r *http.Request) {
tok, err := auth.Token(state, r)
if err != nil {
http.Error(w, "Couldn't get token", http.StatusForbidden)
}
if st := r.FormValue("state"); st != state {
http.NotFound(w, r)
}
// use the token to get an authenticated client
client := auth.NewClient(tok)
_, err = fmt.Fprintf(w, "Login Completed!")
if err != nil {
return
}
tempClientChan <- &client
}
// NewWidget creates a new widget for WTF
func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages *tview.Pages, settings *Settings) *Widget {
redirectURI = "http://localhost:" + settings.callbackPort + "/callback"
auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadCurrentlyPlaying, spotify.ScopeUserReadPlaybackState, spotify.ScopeUserModifyPlaybackState)View on GitHub (pinned to bb838c1ccb)
Solutions
- Re-run the Spotify authentication flow from scratch: restart wtfutil and open the auth URL fresh, then approve access on the consent screen
- Check that the redirect URI and port in the Spotify app settings and the module config match the local auth server port
- Make sure you complete authorization in one session — an old/stale callback tab will have an outdated state parameter
- Fix the handler to return after http.Error and handle a nil token so a failed exchange doesn't cascade into a nil-pointer panic: add `return` after each http.Error/NotFound call
Example fix
// before
tok, err := auth.Token(state, r)
if err != nil {
http.Error(w, "Couldn't get token", http.StatusForbidden)
}
if st := r.FormValue("state"); st != state {
http.NotFound(w, r)
}
client := auth.NewClient(tok)
// after
tok, err := auth.Token(state, r)
if err != nil {
http.Error(w, "Couldn't get token", http.StatusForbidden)
return
}
if st := r.FormValue("state"); st != state {
http.NotFound(w, r)
return
}
client := auth.NewClient(tok) Defensive patterns
Strategy: fallback
Validate before calling
// before trusting the flow, confirm the callback request is well-formed:
func validCallback(r *http.Request, expectedState string) bool {
if r.FormValue("error") != "" {
return false
}
return r.FormValue("state") == expectedState && r.FormValue("code") != ""
} Try / catch
tok, err := auth.Token(state, r)
if err != nil {
http.Error(w, "Couldn't get token", http.StatusForbidden)
return
}
client := auth.NewClient(tok)
ctx := context.Background()
if _, err := client.CurrentUser(ctx); err != nil {
log.Printf("spotify auth incomplete, retry flow: %v", err)
return
} Prevention
- Complete the Spotify consent screen in the same browser session that initiated it; don't reuse stale callback tabs
- Ensure the Spotify app's redirect URI exactly matches the local callback URL (scheme, host, port)
- Approve the requested scopes — denying consent produces an error callback
- Restart the auth flow after any wtfutil restart, since the state parameter is regenerated per run
When it happens
Trigger: The browser callback hits the local auth server but auth.Token fails: Spotify returns an error parameter in the redirect (user denied access), the code/state mismatch, or the request lacks a valid code. Also fires when the state FormValue doesn't match the expected state, though that path responds with 404 without returning.
Common situations: User cancels or rejects the Spotify authorization consent screen; the OAuth redirect arrives at a different port than the one the auth server listens on; stale browser tab replays an old callback after state was regenerated on a restart; network errors during the token exchange with Spotify's API.
Related errors
- authentication failed! Please log in to Spotify by visiting
- extracting player state failed! Please refresh or restart WT
- executing request: %w
- unexpected status %d from DEV.to API
- %s
AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03).
Data as JSON: /api/errors/2a683fc2a89dae07.
Report an issue: GitHub.