{"record":{"id":"5516d38b2a7ab9a0","repo":"DIYgod/RSSHub","slug":"zhihu-jsdom-did-not-provide-document-cookie-acces","errorCode":null,"errorMessage":"zhihu: JSDOM did not provide document.cookie accessors","messagePattern":"zhihu: JSDOM did not provide document\\.cookie accessors","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/routes/zhihu/utils.ts","lineNumber":157,"sourceCode":"        runScripts: 'outside-only',\n        pretendToBeVisual: true,\n        virtualConsole: new VirtualConsole(),\n    });\n    const { window } = dom;\n    Object.defineProperties(window.navigator, {\n        userAgent: { value: ua, configurable: true },\n        webdriver: { value: false, configurable: true },\n    });\n    window.TextEncoder = TextEncoder;\n    window.TextDecoder = TextDecoder as typeof window.TextDecoder;\n    window.atob = (value: string) => Buffer.from(value, 'base64').toString('binary');\n    window.btoa = (value: string) => Buffer.from(value, 'binary').toString('base64');\n    Object.assign(window, { __g: {} });\n\n    const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie');\n    if (!cookieDescriptor?.get || !cookieDescriptor.set) {\n        window.close();\n        throw new Error('zhihu: JSDOM did not provide document.cookie accessors');\n    }\n    const tokenPromise = new Promise<string>((resolve) => {\n        Object.defineProperty(window.document, 'cookie', {\n            configurable: true,\n            get: cookieDescriptor.get,\n            set(value: string) {\n                Reflect.apply(cookieDescriptor.set!, window.document, [value]);\n                const token = value.match(/__zse_ck=([^;]+)/)?.[1];\n                if (token?.includes('-')) {\n                    resolve(token);\n                }\n            },\n        });\n    });\n\n    let zseCk: string | undefined;\n    try {\n        // Zhihu's challenge is intentionally delivered as executable JavaScript.","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/zhihu/utils.ts#L139-L175","documentation":"Thrown when the JSDOM environment constructed to execute Zhihu's challenge script does not expose `document.cookie` getter/setter accessors on `Document.prototype`. The generator needs to intercept `document.cookie` writes to capture the `__zse_ck` token the script sets, so if JSDOM's cookie descriptor is missing or non-functional, it closes the window and aborts.","triggerScenarios":"The installed `jsdom` version changed or removed its `document.cookie` property descriptor (different JSDOM major version, a stricter cookie config, or `Storage`/cookie access disabled), so `Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie')` returns undefined or an accessor-less descriptor.","commonSituations":"A dependency upgrade (jsdom) altered how `document.cookie` is defined; running under a runtime/Node version where JSDOM behaves differently; the `JSDOM` constructor options (e.g. `url`, `runScripts`) were changed in a way that disables cookie support.","solutions":["Pin or upgrade `jsdom` to a version known to expose a functional `document.cookie` accessor on `Document.prototype` (check the version currently working in RSSHub's lockfile).","Ensure the JSDOM instance is constructed with a real `url` and `runScripts: 'outside-only'` so cookie APIs are initialized.","If the new JSDOM defines `cookie` elsewhere (e.g. on `window.document` directly rather than the prototype), adjust the descriptor lookup target accordingly."],"exampleFix":"// before\nconst cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie');\nif (!cookieDescriptor?.get || !cookieDescriptor.set) {\n    window.close();\n    throw new Error('zhihu: JSDOM did not provide document.cookie accessors');\n}\n// after — fall back to the instance if the prototype has no descriptor\nconst cookieDescriptor =\n    Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie') ??\n    Object.getOwnPropertyDescriptor(window.document, 'cookie');\nif (!cookieDescriptor?.get || !cookieDescriptor.set) {\n    window.close();\n    throw new Error('zhihu: JSDOM did not provide document.cookie accessors');\n}","handlingStrategy":"fallback","validationCode":"// Verify JSDOM exposes cookie accessors before relying on the VM challenge\nimport { JSDOM } from 'jsdom';\nfunction jsdomSupportsCookieAccessor() {\n    const dom = new JSDOM('<!doctype html><html></html>', { url: 'https://example.com/' });\n    const d = Object.getOwnPropertyDescriptor(dom.window.Document.prototype, 'cookie');\n    dom.window.close();\n    return !!(d?.get && d.set);\n}","typeGuard":null,"tryCatchPattern":"const cookieDescriptor = Object.getOwnPropertyDescriptor(window.Document.prototype, 'cookie')\n    ?? Object.getOwnPropertyDescriptor(window.document, 'cookie');\nif (!cookieDescriptor?.get || !cookieDescriptor.set) {\n    window.close();\n    // fall back to configured credentials instead of failing\n    if (configuredDc0) return { dc0: configuredDc0 };\n    throw new Error('zhihu: JSDOM did not provide document.cookie accessors');\n}","preventionTips":["Pin the jsdom version that RSSHub's lockfile certifies; don't float the major.","Add a unit test asserting JSDOM exposes a functional document.cookie accessor on the prototype.","When upgrading jsdom, re-run the Zhihu challenge generation path in CI before shipping."],"tags":["zhihu","anti-bot","jsdom","dependency","environment"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}