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

  1. Re-run the Spotify authentication flow from scratch: restart wtfutil and open the auth URL fresh, then approve access on the consent screen
  2. Check that the redirect URI and port in the Spotify app settings and the module config match the local auth server port
  3. Make sure you complete authorization in one session — an old/stale callback tab will have an outdated state parameter
  4. 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

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


AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03). Data as JSON: /api/errors/2a683fc2a89dae07. Report an issue: GitHub.