DIYgod/RSSHub · error · Error
Failed to retrieve refresh token from MangaDex API.
Error message
Failed to retrieve refresh token from MangaDex API.
What it means
Thrown after POSTing credentials to the MangaDex OpenID token endpoint (auth.mangadex.org/realms/mangadex/protocol/openid-connect/token) with grant_type=password when the parsed body is missing either refresh_token or access_token. It means the request did not raise an HTTP error but the returned payload is not a valid token response (often Keycloak returns an error object with HTTP 200). This guards the assumption that a successful password grant yields both tokens.
Source
Thrown at lib/routes/mangadex/_access.ts:77
const response = await got.post(constants.API.TOKEN, {
headers: {
'User-Agent': config.trueUA,
},
form: {
grant_type: 'password',
username: config.mangadex.username,
password: config.mangadex.password,
client_id: config.mangadex.clientId,
client_secret: config.mangadex.clientSecret,
},
});
const refreshToken = response?.data?.refresh_token;
const accessToken = response?.data?.access_token;
if (!refreshToken || !accessToken) {
throw new Error('Failed to retrieve refresh token from MangaDex API.');
}
config.mangadex.refreshToken = refreshToken; // cache the refresh token
return accessToken;
};
const getAccessTokenByRefreshToken = async () => {
if (!config.mangadex.clientId || !config.mangadex.clientSecret) {
throw new ConfigNotFoundError('Cannot get access token since MangaDex client ID or secret is not set.');
}
if (!config.mangadex.refreshToken) {
throw new ConfigNotFoundError('Cannot get access token since MangaDex refresh token is not set.');
}
const response = await got.post(constants.API.TOKEN, {
headers: {
'User-Agent': config.trueUA,View on GitHub (pinned to bed535e087)
Solutions
- Verify MANGADEX_USERNAME and MANGADEX_PASSWORD are set in the environment and the password is still valid by logging in at mangadex.org.
- Confirm MANGADEX_CLIENT_ID and MANGADEX_CLIENT_SECRET match a Personal API Client created under mangadex.org/settings/options (not the legacy API client).
- Temporarily log response.data (and response.statusCode) right before the throw to see the Keycloak error_description, then fix the specific cause.
- If the account uses 2FA, generate a personal client + refresh token out-of-band and set MANGADEX_REFRESH_TOKEN instead of username/password.
- If MangaDex changed the token payload shape, update _access.ts to read the new field path.
Example fix
// before
if (!refreshToken || !accessToken) {
throw new Error('Failed to retrieve refresh token from MangaDex API.');
}
// after
if (!refreshToken || !accessToken) {
const detail = response?.data?.error_description || JSON.stringify(response?.data);
throw new Error(`Failed to retrieve refresh token from MangaDex API: ${detail}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: ensure the password-grant inputs are present
const need = ['MANGADEX_USERNAME','MANGADEX_PASSWORD','MANGADEX_CLIENT_ID','MANGADEX_CLIENT_SECRET'];
const missing = need.filter((k) => !process.env[k]);
if (missing.length) {
throw new Error('Missing MangaDex env: ' + missing.join(', '));
} Type guard
interface MangadexTokenResponse {
access_token?: string;
refresh_token?: string;
error_description?: string;
}
const isTokenResponse = (v: unknown): v is MangadexTokenResponse =>
typeof v === 'object' && v !== null &&
typeof (v as MangadexTokenResponse).access_token === 'string'; Try / catch
// in the caller of getToken / getAccessTokenByUserCredentials
try {
token = await getToken();
} catch (e) {
// re-auth path or surface a config-flavored error to the operator
throw new ConfigNotFoundError('MangaDex auth failed; check MANGADEX_* credentials: ' + (e as Error).message);
} Prevention
- Store all MANGADEX_* credentials as env vars rather than editing config.ts.
- Validate credentials with a one-off curl to the token endpoint before enabling the route.
- Set MASTODON-style monitoring on the mangadex:access-token cache key to detect repeated auth failures.
When it happens
Trigger: POST to constants.API.TOKEN with username/password/client_id/client_secret where response.data.refresh_token or response.data.access_token is undefined, null, or empty string. Happens when Keycloak rejects credentials inside the body, the account is 2FA-locked, clientId/clientSecret are wrong, or the response shape changed.
Common situations: MANGADEX_USERNAME / MANGADEX_PASSWORD env vars unset or stale; password containing special characters that were not encoded into form fields; clientId/clientSecret that do not match a registered MangaDex personal client; account with mandatory 2FA; MangaDex Keycloak realm temporarily returning error payloads with 200 status.
Related errors
- Failed to retrieve access token from MangaDex API.
- Cannot get access token since MangaDex client ID or secret i
- Cannot get refresh token since MangaDex username or password
- Cannot get access token since MangaDex refresh token is not
- Failed to retrieve user settings from MangaDex API.
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/cf729ce02a4707a3.
Report an issue: GitHub.