{"record":{"id":"85fc1a670c43c80f","repo":"DIYgod/RSSHub","slug":"no-response-received-from-apiurl","errorCode":null,"errorMessage":"No response received from ${apiUrl}","messagePattern":"No response received from (.+?)","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/fortnite/news.ts","lineNumber":60,"sourceCode":"    // Use Playwright instead of got, which may be blocked by anti-crawling scripts with response code 403.\n    const context = await playwright();\n    const page = await context.newPage();\n\n    // only document is allowed\n    await page.route('**/*', (route) => {\n        const request = route.request();\n        request.resourceType() === 'document' ? route.continue() : route.abort();\n    });\n\n    // log manually (necessary for Playwright)\n    logger.http(`Requesting ${apiUrl}`);\n    let data;\n    try {\n        const response = await page.goto(apiUrl, {\n            waitUntil: 'networkidle',\n        });\n        if (!response) {\n            throw new Error(`No response received from ${apiUrl}`);\n        }\n        if (!response.ok()) {\n            const statusText = response.statusText();\n            const statusMessage = [response.status(), statusText].filter(Boolean).join(' ');\n            throw new Error(`Fortnite API responded with ${statusMessage} for ${apiUrl}`);\n        }\n        const contentType = response.headers()['content-type'];\n        if (!contentType?.includes('application/json')) {\n            throw new Error(`Fortnite API returned non-JSON response with content-type ${contentType ?? 'unknown'}`);\n        }\n\n        data = await response.json();\n    } finally {\n        await page.close();\n        await context.close();\n    }\n\n    const { blogList: list } = data;","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/fortnite/news.ts#L42-L78","documentation":"Playwright's page.goto() resolves to the main request's response object, or null when navigation produces no response at all (the browser could not obtain any HTTP response for the top-level document). The Fortnite news route treats a null response as a hard failure because there is no status code or body to inspect. This is distinct from a non-OK status (handled separately) and indicates the request never completed at the transport level.","triggerScenarios":"The target apiUrl is unreachable due to DNS failure, a TCP/TLS connection error, a browser-level navigation timeout before any bytes arrived, or the page being closed/aborted by a request interceptor that mishandled the document resource type. Also occurs when Fortnite's API endpoint is decommissioned or returns a redirect loop.","commonSituations":"Running in an environment without outbound network access; the request interceptor (resourceType() === 'document' ? continue : abort) accidentally blocking the document; waitUntil: 'networkidle' timing out on a page that never goes idle; Fortnite rotating or deprecating the API URL.","solutions":["Confirm the apiUrl is still valid by curling it directly outside Playwright.","Check the route.abort/continue interceptor ensures document requests for the apiUrl host are allowed through.","Lower or adjust waitUntil to 'domcontentloaded' if 'networkidle' never settles, and increase the navigation timeout.","Verify outbound network/DNS from the RSSHub host."],"exampleFix":"// before\nconst response = await page.goto(apiUrl, { waitUntil: 'networkidle' });\nif (!response) {\n    throw new Error(`No response received from ${apiUrl}`);\n}\n\n// after\nconst response = await page.goto(apiUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });\nif (!response) {\n    throw new Error(`No response received from ${apiUrl} (navigation aborted, timed out, or blocked by interceptor)`);\n}","handlingStrategy":"retry","validationCode":"// pre-flight: confirm host resolves before launching Playwright\nimport { resolve } from 'node:dns/promises';\ntry { await resolve(new URL(apiUrl).hostname); } catch { throw new Error('DNS resolution failed'); }","typeGuard":null,"tryCatchPattern":"try {\n  const response = await page.goto(apiUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });\n  if (!response) throw new Error(`No response from ${apiUrl}`);\n} catch (e) {\n  // retry once with a fresh context, then surface\n  throw e;\n}","preventionTips":["Ensure the request interceptor allows document requests to the API host.","Prefer 'domcontentloaded' over 'networkidle' to avoid spurious null responses.","Set an explicit navigation timeout."],"tags":["playwright","network","browser"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}