{"record":{"id":"ee4073244bf75716","repo":"jackwener/OpenCLI","slug":"zhihu-column-download-returned-malformed-article-f","errorCode":null,"errorMessage":"Zhihu column download returned malformed article fields","messagePattern":"Zhihu column download returned malformed article fields","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/zhihu/download-helpers.js","lineNumber":116,"sourceCode":"        for (const name of ['data-original', 'data-actualsrc', 'data-src']) img.removeAttribute(name);\n        if (!src) img.removeAttribute('src');\n        else {\n            img.setAttribute('src', src);\n            if (!seen.has(src)) {\n                seen.add(src);\n                imageUrls.push(src);\n            }\n        }\n    });\n    return { contentHtml: root.innerHTML, imageUrls };\n}\n\nfunction requireArticle(raw) {\n    const data = unwrapEvaluateResult(raw);\n    if (!data || typeof data !== 'object' || Array.isArray(data)\n        || typeof data.title !== 'string' || typeof data.contentHtml !== 'string'\n        || !Array.isArray(data.imageUrls) || !data.imageUrls.every((url) => typeof url === 'string')) {\n        throw new CommandExecutionError('Zhihu column download returned malformed article fields');\n    }\n    if (!data.contentHtml.trim()) {\n        throw new EmptyResultError('zhihu download', 'The Zhihu column article had no exportable content.');\n    }\n    return data;\n}\n\nexport async function extractColumnArticle(page, target) {\n    await page.goto(target.url);\n    await page.wait(3);\n    const normalize = `(${normalizeContentImages.toString()})`;\n    const raw = await page.evaluate(`\n      (() => {\n        const content = document.querySelector('.Post-RichTextContainer, .RichText, .ArticleContent');\n        const normalized = ${normalize}(content?.innerHTML || '');\n        return {\n          title: document.querySelector('.Post-Title, h1.ContentItem-title, .ArticleTitle')?.textContent?.trim() || 'untitled',\n          author: document.querySelector('.AuthorInfo-name, .UserLink-link')?.textContent?.trim() || '',","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zhihu/download-helpers.js#L98-L134","documentation":"During `zhihu download` of a column article, requireArticle validates the shape of the data returned by the in-page extraction (via unwrapEvaluateResult): it must be a non-array object with string `title`, string `contentHtml`, and an array of string `imageUrls`. CommandExecutionError (code COMMAND_EXEC) is thrown when any field is missing or of the wrong type, guarding downstream export code from malformed page data.","triggerScenarios":"The page.evaluate/extract script returns undefined, null, a non-serializable value, or an object whose title/contentHtml/imageUrls do not match the expected types — typically because Zhihu changed the column article DOM so the extraction selectors return undefined fields.","commonSituations":"Zhihu column pages redesigned so `contentHtml` comes back undefined; the extraction returning an array instead of an object; a lazy-loaded page where the script runs before content mounts; unwrapping a browser evaluate result that was an exception payload rather than data.","solutions":["Log the raw value returned by the in-page extraction (before requireArticle) to see which field is missing/mistyped.","Update the extraction script's selectors to the current Zhihu column article DOM so title/contentHtml/imageUrls are populated.","Ensure the page is fully loaded before extraction (wait for the article content node) so fields are not undefined.","Confirm unwrapEvaluateResult is receiving the data payload, not an error/exception envelope from the evaluate call."],"exampleFix":"// before: assuming extraction always returns full shape\nconst data = await page.evaluate(extractScript);\nexportArticle(data);\n// after: pre-check before calling requireArticle\nconst data = await page.evaluate(extractScript);\nif (!data || typeof data.contentHtml !== 'string') {\n  await page.wait(3); // content may not have mounted yet\n}\nconst article = requireArticle(await page.evaluate(extractScript));","handlingStrategy":"type-guard","validationCode":"const raw = await page.evaluate(extractScript);\nif (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('Extraction returned no article object');","typeGuard":"function isArticleData(v) {\n  return v != null && typeof v === 'object' && !Array.isArray(v)\n    && typeof v.title === 'string'\n    && typeof v.contentHtml === 'string'\n    && Array.isArray(v.imageUrls) && v.imageUrls.every((u) => typeof u === 'string');\n}","tryCatchPattern":"try {\n  const article = requireArticle(await page.evaluate(extractScript));\n} catch (e) {\n  if (e.code === 'COMMAND_EXEC' && /malformed article fields/i.test(e.message)) {\n    console.error('Zhihu DOM may have changed — dump raw extraction for inspection');\n  }\n  throw e;\n}","preventionTips":["Wait for the article content node to mount before running the extraction script.","When Zhihu updates its column pages, re-verify extraction selectors return title/contentHtml/imageUrls.","Validate the evaluate result shape before passing it to export code."],"tags":["zhihu","schema-validation","scraping","dom-change"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}