wtfutil/wtf · critical
panic(err)
Error message
panic(err)
What it means
The Spotify widget's first-run authorization flow waits on tempClientChan, then calls client.CurrentUser() to verify the token; if that call fails it panics with the raw error. Typical causes are the Spotify auth handshake failing (bad/missing client ID/secret, callback timeout, user denying consent) or the token being invalid/expired at first call. This is module initialization code, so a panic kills the whole app.
Source
Thrown at modules/spotifyweb/widget.go:100
settings: settings,
}
http.HandleFunc("/callback", authHandler)
go func() {
err := http.ListenAndServe(":"+callbackPort, nil)
if err != nil {
return
}
}()
go func() {
// wait for auth to complete
client = <-tempClientChan
// use the client to make calls that require authorization
_, err := client.CurrentUser()
if err != nil {
panic(err)
}
playerState, err = client.PlayerState()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
widget.client = client
widget.playerState = playerState
widget.Refresh()
}()
// While I wish I could find the reason this doesn't work, I can't.
//
// Normally, this should open the URL to the browser, however it opens the Explorer window in Windows.
// This mostly likely has to do with the fact that the URL includes some very special characters that no terminal likes.
// The only solution would be to include quotes in the command, which is why I do here, but it doesn't work.View on GitHub (pinned to bb838c1ccb)
Solutions
- Complete the Spotify authentication flow in the browser when prompted and ensure the callback reaches the app
- Verify clientID/clientSecret in the Spotify widget config match your app at developer.spotify.com
- Delete cached Spotify tokens/credentials and re-authenticate
- Check network access to accounts.spotify.com and api.spotify.com (proxy/firewall); fix clock skew if tokens expire immediately
- Wrap or patch the panic path if running unattended so failures degrade gracefully instead of crashing the app
Example fix
// before
_, err := client.CurrentUser()
if err != nil {
panic(err)
}
// after
_, err := client.CurrentUser()
if err != nil {
fmt.Printf("spotify auth failed: %v\n", err)
return // skip widget instead of crashing
} Defensive patterns
Strategy: try-catch
Validate before calling
// before starting the widget, check credentials are present
if cfg.Spotify.ClientID == "" || cfg.Spotify.ClientSecret == "" {
return fmt.Errorf("spotify widget requires clientID and clientSecret")
} Try / catch
// guard module startup against the panic
func startSpotify(wg *sync.WaitGroup) {
defer func() {
if r := recover(); r != nil {
log.Printf("spotify widget disabled, auth failed: %v", r)
}
}()
wg.Add(1)
makeModule(wg, makeSpotifyWidget)
} Prevention
- Complete the OAuth browser flow on first run; don't run headless without a cached token
- Keep clientID/clientSecret current at developer.spotify.com
- Clear stale cached tokens after credential rotation
- Ensure outbound access to accounts.spotify.com and api.spotify.com
When it happens
Trigger: Widget starts without a stored valid token: it blocks on tempClientChan for auth, then CurrentUser() returns an error — expired/refresh-failed token, wrong clientID/secret in config, auth server unreachable, or the user never completed the browser auth flow.
Common situations: First run without completing the Spotify OAuth login; Spotify app credentials rotated or revoked; system clock skew invalidating tokens; network/proxy blocking accounts.spotify.com; running headless where the callback can't complete.
Related errors
AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03).
Data as JSON: /api/errors/b6e59ee292bb44da.
Report an issue: GitHub.