{"record":{"id":"7a35a8571458649e","repo":"DIYgod/RSSHub","slug":"spotify-private-rss-is-disabled-due-to-the-lack-of","errorCode":null,"errorMessage":"Spotify private RSS is disabled due to the lack of <a href=\"https://docs.rsshub.app/deploy/config#route-specific-configurations\">relevant config</a>","messagePattern":"Spotify private RSS is disabled due to the lack of <a href=\"https://docs\\.rsshub\\.app/deploy/config#route-specific-configurations\">relevant config</a>","errorType":"exception","errorClass":"ConfigNotFoundError","httpStatus":503,"severity":"error","filePath":"lib/routes/spotify/utils.ts","lineNumber":30,"sourceCode":"\n    const tokenResponse = await ofetch('https://accounts.spotify.com/api/token', {\n        method: 'POST',\n        headers: {\n            Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,\n            'Content-Type': 'application/x-www-form-urlencoded',\n        },\n        body: new URLSearchParams({\n            grant_type: 'client_credentials',\n        }).toString(),\n    });\n    return tokenResponse.access_token;\n}\n\n// Token used to retrieve user-specific information.\n// Note that we don't use PKCE since the client secret shall be safe on the server.\nasync function getPrivateToken() {\n    if (!config.spotify || !config.spotify.clientId || !config.spotify.clientSecret || !config.spotify.refreshToken) {\n        throw new ConfigNotFoundError('Spotify private RSS is disabled due to the lack of <a href=\"https://docs.rsshub.app/deploy/config#route-specific-configurations\">relevant config</a>');\n    }\n\n    const { clientId, clientSecret, refreshToken } = config.spotify;\n\n    const tokenResponse = await ofetch('https://accounts.spotify.com/api/token', {\n        method: 'POST',\n        headers: {\n            Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,\n            'Content-Type': 'application/x-www-form-urlencoded',\n        },\n        body: new URLSearchParams({\n            grant_type: 'refresh_token',\n            refresh_token: refreshToken,\n        }).toString(),\n    });\n    return tokenResponse.access_token;\n}\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/spotify/utils.ts#L12-L48","documentation":"Thrown as a ConfigNotFoundError from the getPrivateToken() utility function when config.spotify is missing clientId, clientSecret, OR refreshToken. This function is called by Spotify routes that access user-specific data (e.g. /spotify/top/tracks, /spotify/top/artists) using the refresh-token OAuth flow. The refreshToken is a long-lived credential obtained via the Spotify authorization-code flow. This is a stricter check than getPublicToken (error 517) because it additionally requires the refresh token.","triggerScenarios":"Any Spotify private-data route is requested (e.g. /spotify/top/tracks) on an instance where SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, or SPOTIFY_REFRESH_TOKEN is not set. The error fires at token acquisition, before any API call.","commonSituations":"Self-hosted RSSHub with Spotify public credentials but no refresh token; the refresh token was revoked (e.g. user disconnected the app from their Spotify account); or the operator only set the public credentials and did not complete the user-authorization flow.","solutions":["Ensure SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET are set (same as for public routes).","Obtain a SPOTIFY_REFRESH_TOKEN by completing the Spotify authorization-code flow: create an app, set the redirect URI, authorize, and exchange the code for a refresh token.","Set SPOTIFY_REFRESH_TOKEN in the RSSHub environment and restart.","If the refresh token was revoked, re-authorize the app to obtain a new one."],"exampleFix":"# before\n# (SPOTIFY_REFRESH_TOKEN not set)\n\n# after (.env)\nSPOTIFY_CLIENT_ID=your-client-id\nSPOTIFY_CLIENT_SECRET=your-client-secret\nSPOTIFY_REFRESH_TOKEN=AQ...your-refresh-token...","handlingStrategy":"validation","validationCode":"if (!config.spotify?.clientId || !config.spotify?.clientSecret || !config.spotify?.refreshToken) {\n    throw new ConfigNotFoundError('Spotify private RSS requires SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, and SPOTIFY_REFRESH_TOKEN.');\n}","typeGuard":"function hasSpotifyPrivateConfig(cfg: typeof config): cfg is typeof config & { spotify: { clientId: string; clientSecret: string; refreshToken: string } } {\n    return (\n        !!cfg.spotify &&\n        typeof cfg.spotify.clientId === 'string' &&\n        typeof cfg.spotify.clientSecret === 'string' &&\n        typeof cfg.spotify.refreshToken === 'string' &&\n        cfg.spotify.refreshToken.length > 0\n    );\n}","tryCatchPattern":null,"preventionTips":["Complete the full Spotify authorization-code flow to obtain a refresh token, not just client credentials.","Store the refresh token securely and document the authorization steps for operators.","If the refresh token is revoked, have a documented re-authorization procedure ready."],"tags":["config","authentication","spotify","env-vars","oauth"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}