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.

Solutions

  1. Complete the Spotify authentication flow in the browser when prompted and ensure the callback reaches the app
  2. Verify clientID/clientSecret in the Spotify widget config match your app at developer.spotify.com
  3. Delete cached Spotify tokens/credentials and re-authenticate
  4. Check network access to accounts.spotify.com and api.spotify.com (proxy/firewall); fix clock skew if tokens expire immediately
  5. 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

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.

Appendix: 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)