{"record":{"id":"801170c330a0ceba","repo":"jackwener/OpenCLI","slug":"failed-to-read-xiaoe-catalog-message","errorCode":null,"errorMessage":"Failed to read xiaoe catalog: ${message}","messagePattern":"Failed to read xiaoe catalog: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaoe/catalog.js","lineNumber":196,"sourceCode":"        resource_id: resId,\n        url: urlPath ? origin + urlPath + resId + '?type=2' : '',\n        status: child.is_finish === 1 ? '已完成' : (child.learn_progress > 0 ? child.learn_progress + '%' : '未学'),\n      });\n    }\n  }\n  return result;\n})()`;\n}\n\nasync function getXiaoeCatalog(page, args) {\n    const url = requireXiaoePageUrl(args.url, 'catalog');\n    let rows;\n    try {\n        await page.goto(url, { waitUntil: 'load', settleMs: 8000 });\n        rows = await page.evaluate(buildCatalogScript());\n    } catch (error) {\n        const message = error instanceof Error ? error.message : String(error);\n        throw new CommandExecutionError(\n            `Failed to read xiaoe catalog: ${message}`,\n            'page may not have rendered or auth may be required',\n        );\n    }\n    if (!Array.isArray(rows) || rows.length === 0) {\n        throw new EmptyResultError(\n            'xiaoe/catalog',\n            'No catalog rows extracted — the URL may not be a course page or the login session has expired',\n        );\n    }\n    return rows;\n}\n\nexport const catalogCommand = cli({\n    site: 'xiaoe',\n    name: 'catalog',\n    access: 'read',\n    description: '小鹅通课程目录（支持普通课程、专栏、大专栏）',","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoe/catalog.js#L178-L214","documentation":"getXiaoeCatalog wraps page.goto(url) plus the injected catalog extraction script in try/catch; any navigation or evaluation failure is rethrown as CommandExecutionError('Failed to read xiaoe catalog: <original message>') with the hint that the page may not have rendered or auth may be required.","triggerScenarios":"page.goto(url,{waitUntil:'load',settleMs:8000}) throws (net error, timeout, navigation aborted) or page.evaluate(buildCatalogScript()) throws inside the page context, for a catalog URL.","commonSituations":"Expired login causing server-side redirect/error page whose DOM the script cannot parse; network/DNS failure or corporate proxy blocking the domain; catalog page changed markup so the injected script throws (e.g. null selector access); 8s settle too short for a slow SPA.","solutions":["Read the inner message to identify the root cause (navigation vs evaluate failure).","Re-authenticate via the xiaoe login flow if the session expired, then retry.","Retry on transient network errors / increase settleMs for slow pages.","If the inner error is a script TypeError, update buildCatalogScript() for changed catalog markup."],"exampleFix":"// before\nrows = await page.evaluate(buildCatalogScript());\n// after\ntry {\n  rows = await page.evaluate(buildCatalogScript());\n} catch (e) {\n  await runXiaoeLogin(page); // refresh session before giving up\n  rows = await page.evaluate(buildCatalogScript());\n}","handlingStrategy":"try-catch","validationCode":"await assertXiaoeAuthed(page); // ensure session before navigation\nnew URL(url); // throws early on a malformed URL","typeGuard":"function isHttpsUrl(u) { try { return new URL(u).protocol === 'https:'; } catch { return false; } }","tryCatchPattern":"try {\n  rows = await getXiaoeCatalog(page, url);\n} catch (e) {\n  if (e instanceof CommandExecutionError && /Failed to read xiaoe catalog/.test(e.message)) {\n    await runXiaoeLogin(page);\n    rows = await getXiaoeCatalog(page, url); // one retry after re-auth\n  } else throw e;\n}","preventionTips":["Verify the session before navigating to catalog pages.","Retry once on transient navigation/evaluation errors.","Check the inner message to distinguish network vs script failures."],"tags":["scraping","page-evaluation","network","xiaoe"],"backgroundTag":"page-render-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}