{"record":{"id":"01a66912bb003a8d","repo":"jackwener/OpenCLI","slug":"zhihu-column-extraction-failed-error-instanceof","errorCode":null,"errorMessage":"Zhihu column extraction failed: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Zhihu column extraction failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/zhihu/download-helpers.js","lineNumber":140,"sourceCode":"}\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() || '',\n          publishTime: document.querySelector('.ContentItem-time, .Post-Time')?.textContent?.trim() || '',\n          ...normalized\n        };\n      })()\n    `).catch((error) => {\n        throw new CommandExecutionError(`Zhihu column extraction failed: ${error instanceof Error ? error.message : String(error)}`);\n    });\n    return requireArticle(raw);\n}\n\nexport async function extractAnswer(page, target) {\n    try {\n        await page.goto(`https://www.zhihu.com/answer/${target.answerId}`);\n    }\n    catch (error) {\n        throw new CommandExecutionError(\n            `Failed to open Zhihu answer ${target.answerId}: ${error instanceof Error ? error.message : String(error)}`,\n            'Open the answer URL in Chrome and retry after the page is reachable.',\n        );\n    }\n    const currentUrl = typeof page.getCurrentUrl === 'function' ? await page.getCurrentUrl().catch(() => '') : '';\n    const currentTarget = parseAnswerTarget(currentUrl);\n    if (!currentTarget || currentTarget.answerId !== target.answerId || !currentTarget.questionId\n        || (target.questionId && currentTarget.questionId && target.questionId !== currentTarget.questionId)) {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zhihu/download-helpers.js#L122-L158","documentation":"extractColumnArticle drives a connected Chrome page to zhuanlan.zhihu.com, waits 3 seconds, then runs an in-page evaluate script that scrapes title/author/publishTime and normalizes content images. If that evaluate call rejects (script error, page closed, navigation interrupted, selector crash), the promise's .catch rethrows it as a CommandExecutionError with the underlying message appended. It is a wrapper error: the real cause is in the appended message.","triggerScenarios":"Calling extractColumnArticle(page, target) when page.evaluate throws: the Browser Bridge tab was closed mid-scrape, the page navigated away or redirected (e.g. to a login/captcha wall) before the script ran, the in-page IIFE threw, or the bridge connection dropped during the 3s wait.","commonSituations":"Chrome was quit or the tab closed while the CLI ran; Zhihu redirected the column URL to an error/login page; slow network meant the DOM was torn down by a redirect during page.wait(3); an expired login session triggered a redirect the bridge surfaced as a navigation error.","solutions":["Read the appended underlying message; if the tab/page was lost, reconnect the Browser Bridge and rerun the download command.","Open the article URL (https://zhuanlan.zhihu.com/p/<id>) in the connected Chrome profile to confirm it loads and is not behind a login/captcha wall.","Increase tolerance for slow loads: rerun on a stable connection or retry, since transient redirects during page.wait(3) cause this.","Verify the article id/target is valid via parseDownloadTarget before calling; a bad target leads to Zhihu error pages."],"exampleFix":"// before\nconst raw = await extractColumnArticle(page, { kind: 'article', articleId, url });\n// after\nlet raw;\ntry {\n  raw = await extractColumnArticle(page, { kind: 'article', articleId, url });\n} catch (err) {\n  console.error('Column extraction failed:', err.message, '- reconnect Chrome and retry');\n  await bridge.reconnect();\n  raw = await extractColumnArticle(page, { kind: 'article', articleId, url });\n}","handlingStrategy":"try-catch","validationCode":"if (!target || target.kind !== 'article' || !/^\\d+$/.test(target.articleId)) {\n  throw new Error('invalid article target before extraction');\n}\n// ensure the bridge page is alive\nawait page.goto(target.url); // surface navigation errors early, outside the library","typeGuard":"function isArticleTarget(t) {\n  return !!t && typeof t === 'object' && t.kind === 'article'\n    && typeof t.articleId === 'string' && /^\\d+$/.test(t.articleId)\n    && typeof t.url === 'string';\n}","tryCatchPattern":"try {\n  const article = await extractColumnArticle(page, target);\n} catch (err) {\n  if (String(err.message).includes('Zhihu column extraction failed')) {\n    // underlying cause follows the colon — log it, reconnect, retry once\n    await reconnectBridge();\n    return extractColumnArticle(page, target);\n  }\n  throw err;\n}","preventionTips":["Keep the Chrome tab controlled by the bridge open and in the foreground during downloads","Log in to Zhihu in the connected profile to avoid login-wall redirects mid-scrape","Validate targets with parseDownloadTarget before calling extraction functions","Wrap extraction calls in a retry with backoff for transient navigation errors"],"tags":["browser-automation","scraping","navigation","zhihu"],"backgroundTag":"page-evaluate-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}