{"record":{"id":"a4e9691bf77e8311","repo":"DIYgod/RSSHub","slug":"failed-to-parse-embedded-script-json-error-mess","errorCode":null,"errorMessage":"Failed to parse embedded script JSON: ${error.message}","messagePattern":"Failed to parse embedded script JSON: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/sciencedirect/call-for-paper.tsx","lineNumber":41,"sourceCode":"    description: '`sciencedirect.com/browse/calls-for-papers?subject=education` -> `/sciencedirect/call-for-paper/education`',\n};\n\nasync function handler(ctx) {\n    const { subject = '' } = ctx.req.param();\n    const apiUrl = `https://www.sciencedirect.com/browse/calls-for-papers?subject=${subject}`;\n    const response = await got(apiUrl);\n    const $ = load(response.body);\n\n    const scriptJSON = $('script[data-iso-key=\"_0\"]').text();\n    if (!scriptJSON) {\n        throw new Error('Cannot find the script with data-iso-key=\"_0\"');\n    }\n\n    let data;\n    try {\n        data = JSON.parse(JSON.parse(scriptJSON));\n    } catch (error: any) {\n        throw new Error(`Failed to parse embedded script JSON: ${error.message}`, { cause: error });\n    }\n\n    const cfpList = data?.callsForPapers?.list || [];\n    if (!cfpList.length) {\n        throw new Error('No Calls for Papers found');\n    }\n\n    const items = cfpList.map((cfp) => {\n        const link = `https://www.sciencedirect.com/special-issue/${cfp.contentId}/${cfp.url}`;\n        const description = renderToString(\n            <div>\n                <p>\n                    <strong>Summary:</strong> {cfp.summary}\n                </p>\n                <p>\n                    <strong>Submission Deadline:</strong> {cfp.submissionDeadline}\n                </p>\n                <p>","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/sciencedirect/call-for-paper.tsx#L23-L59","documentation":"Thrown when the double JSON.parse of the data-iso script text fails. ScienceRedirect stores its payload as a JSON string inside a JSON envelope (hence JSON.parse(JSON.parse(...))), and any malformed/oddly-encoded content surfaces here with the underlying parse error attached via the cause option.","triggerScenarios":"scriptJSON is non-empty but JSON.parse(JSON.parse(scriptJSON)) throws — the inner parse produces a non-string, or the outer parse hits invalid JSON (e.g. the payload is now single-encoded HTML-escaped, or contains unescaped characters).","commonSituations":"ScienceDirect switched from double-encoded to single-encoded JSON; the payload now contains escaped sequences that break strict parsing; a partial/chunked response produced truncated JSON; cheerio's .text() decoded entities the encoder didn't expect.","solutions":["Log scriptJSON to inspect its actual encoding (single vs double JSON, HTML-escaping).","If single-encoded now, replace JSON.parse(JSON.parse(scriptJSON)) with a single JSON.parse.","If the content is HTML-escaped, unescape (he.decode) before parsing.","Keep the try/catch but include the body length and a snippet in the rethrown error."],"exampleFix":"// before\ntry {\n    data = JSON.parse(JSON.parse(scriptJSON));\n} catch (error: any) {\n    throw new Error(`Failed to parse embedded script JSON: ${error.message}`, { cause: error });\n}\n\n// after — try double, fall back to single encoding\ntry {\n    try {\n        data = JSON.parse(JSON.parse(scriptJSON));\n    } catch {\n        data = JSON.parse(scriptJSON);\n    }\n} catch (error: any) {\n    throw new Error(`Failed to parse embedded script JSON: ${error.message}`, { cause: error });\n}","handlingStrategy":"try-catch","validationCode":"// Normalize the script text (unescape HTML entities) and try single then double parse.\nfunction parseIsoPayload(raw: string): unknown {\n    const unescaped = raw.replace(/&quot;/g, '\"').replace(/&amp;/g, '&');\n    try {\n        return JSON.parse(unescaped);\n    } catch {\n        return JSON.parse(JSON.parse(unescaped));\n    }\n}","typeGuard":"const isPlainObject = (v: unknown): v is Record<string, unknown> =>\n    typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":"let data: unknown;\ntry {\n    data = JSON.parse(JSON.parse(scriptJSON));\n} catch {\n    try {\n        data = JSON.parse(scriptJSON); // single-encoded fallback\n    } catch (error: any) {\n        throw new Error(`Failed to parse embedded script JSON: ${error.message}`, { cause: error });\n    }\n}","preventionTips":["Detect the encoding depth dynamically (try single, then double) instead of assuming double.","Capture a real scriptJSON sample in a fixture to regression-test the parser.","Keep the cause option so the original SyntaxError is preserved for debugging."],"tags":["json-parsing","upstream-change","encoding","sciencedirect","error-handling"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}