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

  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.