DIYgod/RSSHub · error · Error
Failed to retrieve access token from MangaDex API.
Error message
Failed to retrieve access token from MangaDex API.
What it means
Thrown after POSTing grant_type=refresh_token to the MangaDex token endpoint when the response body has no access_token. Unlike an HTTP 400 (which getToken catches and falls back to user credentials), this fires when the endpoint returns 200 with an unusable body, so the automatic fallback does not trigger.
Source
Thrown at lib/routes/mangadex/_access.ts:107
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,
},
form: {
grant_type: 'refresh_token',
refresh_token: config.mangadex.refreshToken,
client_id: config.mangadex.clientId,
client_secret: config.mangadex.clientSecret,
},
});
const accessToken = response?.data?.access_token;
if (!accessToken) {
throw new Error('Failed to retrieve access token from MangaDex API.');
}
return accessToken;
};
export default getToken;
View on GitHub (pinned to bed535e087)
Solutions
- If MANGADEX_USERNAME/PASSWORD are set, let getToken fall back: confirm the FetchError-400 branch fires by checking logs; if it does not, the response is a 200-with-error and you must obtain a fresh refresh token.
- Regenerate the refresh token by performing one password-grant login, then store the returned refresh_token as MANGADEX_REFRESH_TOKEN.
- Verify the refresh token has no leading/trailing whitespace and was issued for the same clientId/clientSecret pair.
- Add response.data inspection before the throw to expose the underlying Keycloak error_description.
Example fix
// before
const accessToken = response?.data?.access_token;
if (!accessToken) {
throw new Error('Failed to retrieve access token from MangaDex API.');
}
// after
const accessToken = response?.data?.access_token;
if (!accessToken) {
const detail = response?.data?.error_description || JSON.stringify(response?.data);
throw new Error(`Failed to retrieve access token from MangaDex API: ${detail}`);
} Defensive patterns
Strategy: retry
Validate before calling
if (!config.mangadex.refreshToken) {
// getToken will take the password path instead; no point validating a missing token
return;
}
// a refresh token is a Keycloak JWT; cheap sanity check on shape
const parts = config.mangadex.refreshToken.split('.');
if (parts.length < 2) {
throw new Error('MANGADEX_REFRESH_TOKEN does not look like a JWT');
} Type guard
const isRefreshTokenBody = (v: unknown): v is { access_token: string } =>
typeof v === 'object' && v !== null && typeof (v as { access_token?: unknown }).access_token === 'string'; Try / catch
try {
token = await getToken(); // getToken already retries via password grant on HTTP 400
} catch (e) {
if (/access token/i.test((e as Error).message)) {
// refresh token likely expired; clear and let the next request re-auth
delete config.mangadex.refreshToken;
}
throw e;
} Prevention
- Treat refresh tokens as expiring credentials; rotate them periodically.
- Always pair MANGADEX_REFRESH_TOKEN with username/password so getToken can fall back.
- Trim whitespace when pasting tokens to avoid silent failures.
When it happens
Trigger: POST to constants.API.TOKEN with grant_type=refresh_token where response.data.access_token is undefined/null. The refresh token is expired, revoked, belongs to another client, or MangaDex returned an error object with a non-400 status.
Common situations: Cached MANGADEX_REFRESH_TOKEN older than 30 days (MangaDex refresh tokens expire); refresh token minted under a different clientId; refresh token pasted with trailing whitespace; Keycloak returned an error body but with status 200.
Related errors
- Failed to retrieve refresh 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/a54ac89a01a41430.
Report an issue: GitHub.