DIYgod/RSSHub · error · Error
Fortnite API returned non-JSON response with content-type ${
Error message
Fortnite API returned non-JSON response with content-type ${contentType ?? 'unknown'} What it means
Even when the HTTP status is OK, the response may not be JSON. The route inspects the content-type header and requires it to contain 'application/json'; otherwise the body is assumed to be an HTML error page, interstitial, or CAPTCHA, and the route throws with the observed content-type. This guards against silently feeding HTML into JSON parsing downstream.
Source
Thrown at lib/routes/fortnite/news.ts:69
// log manually (necessary for Playwright)
logger.http(`Requesting ${apiUrl}`);
let data;
try {
const response = await page.goto(apiUrl, {
waitUntil: 'networkidle',
});
if (!response) {
throw new Error(`No response received from ${apiUrl}`);
}
if (!response.ok()) {
const statusText = response.statusText();
const statusMessage = [response.status(), statusText].filter(Boolean).join(' ');
throw new Error(`Fortnite API responded with ${statusMessage} for ${apiUrl}`);
}
const contentType = response.headers()['content-type'];
if (!contentType?.includes('application/json')) {
throw new Error(`Fortnite API returned non-JSON response with content-type ${contentType ?? 'unknown'}`);
}
data = await response.json();
} finally {
await page.close();
await context.close();
}
const { blogList: list } = data;
const items = await Promise.all(
list.map((item) =>
cache.tryGet(item.link, () =>
Promise.resolve({
title: item.title,
link: `${rootUrl}/${path}/${item.slug}?lang=${language}`,
pubDate: parseDate(item.date),
author: item.author,View on GitHub (pinned to bed535e087)
Solutions
- Log or return the first 500 bytes of the body to identify whether it is a CAPTCHA, maintenance page, or redirect.
- Set an explicit Accept: application/json header in the Playwright request context / interceptor.
- Check whether a WAF challenge is involved and whether the route needs the Playwright context to solve it before the API call.
- Update the content-type check if the API now serves application/vnd.api+json or similar.
Example fix
// before
const contentType = response.headers()['content-type'];
if (!contentType?.includes('application/json')) {
throw new Error(`Fortnite API returned non-JSON response with content-type ${contentType ?? 'unknown'}`);
}
// after
const contentType = response.headers()['content-type'] ?? '';
if (!contentType.includes('json')) {
const bodySnippet = (await response.text()).slice(0, 200);
throw new Error(`Fortnite API returned non-JSON (${contentType || 'unknown'}). Body preview: ${bodySnippet}`);
} Defensive patterns
Strategy: type-guard
Validate before calling
// set an explicit Accept header in the Playwright context
await context.setExtraHTTPHeaders({ Accept: 'application/json' }); Type guard
const isJsonContentType = (ct: string | undefined): boolean => !!ct && ct.includes('json'); Prevention
- Set Accept: application/json so the server does not negotiate HTML.
- Log a body snippet when content-type mismatches to identify CAPTCHA/maintenance pages.
- Update the check to accept any 'json' subtype (application/vnd.api+json).
When it happens
Trigger: Fortnite's infrastructure returns an HTML maintenance/CAPTCHA page with a 200 status, a CDN/WAF serves an HTML challenge page (Cloudflare, Akamai), the endpoint was replaced with an HTML redirect page, or the API now returns JSON-LD or a different content-type token. The contentType variable holds whatever was sent (or 'unknown' if the header was absent).
Common situations: Anti-bot/WAF challenge pages served before the real API responds; Epic Games rotating to a GraphQL or different content-type; CDN caching an old HTML version; the browser sending an Accept header that triggers HTML negotiation.
Related errors
- Bilibili browser mode did not receive a video list response
- Bilibili browser mode returned unexpected video list API sta
- Bilibili browser mode returned non-JSON response with status
- Got error code ${data.code} while fetching in browser mode:
- Baidu security verification required. The cookie may be expi
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/cc8f9fbd93cdd6ae.
Report an issue: GitHub.