jackwener/OpenCLI · error · CommandExecutionError

Failed to read xiaoe catalog: ${message}

Error message

Failed to read xiaoe catalog: ${message}

What it means

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.

Source

Thrown at clis/xiaoe/catalog.js:196

        resource_id: resId,
        url: urlPath ? origin + urlPath + resId + '?type=2' : '',
        status: child.is_finish === 1 ? '已完成' : (child.learn_progress > 0 ? child.learn_progress + '%' : '未学'),
      });
    }
  }
  return result;
})()`;
}

async function getXiaoeCatalog(page, args) {
    const url = requireXiaoePageUrl(args.url, 'catalog');
    let rows;
    try {
        await page.goto(url, { waitUntil: 'load', settleMs: 8000 });
        rows = await page.evaluate(buildCatalogScript());
    } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        throw new CommandExecutionError(
            `Failed to read xiaoe catalog: ${message}`,
            'page may not have rendered or auth may be required',
        );
    }
    if (!Array.isArray(rows) || rows.length === 0) {
        throw new EmptyResultError(
            'xiaoe/catalog',
            'No catalog rows extracted — the URL may not be a course page or the login session has expired',
        );
    }
    return rows;
}

export const catalogCommand = cli({
    site: 'xiaoe',
    name: 'catalog',
    access: 'read',
    description: '小鹅通课程目录(支持普通课程、专栏、大专栏)',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the inner message to identify the root cause (navigation vs evaluate failure).
  2. Re-authenticate via the xiaoe login flow if the session expired, then retry.
  3. Retry on transient network errors / increase settleMs for slow pages.
  4. If the inner error is a script TypeError, update buildCatalogScript() for changed catalog markup.

Example fix

// before
rows = await page.evaluate(buildCatalogScript());
// after
try {
  rows = await page.evaluate(buildCatalogScript());
} catch (e) {
  await runXiaoeLogin(page); // refresh session before giving up
  rows = await page.evaluate(buildCatalogScript());
}
Defensive patterns

Strategy: try-catch

Validate before calling

await assertXiaoeAuthed(page); // ensure session before navigation
new URL(url); // throws early on a malformed URL

Type guard

function isHttpsUrl(u) { try { return new URL(u).protocol === 'https:'; } catch { return false; } }

Try / catch

try {
  rows = await getXiaoeCatalog(page, url);
} catch (e) {
  if (e instanceof CommandExecutionError && /Failed to read xiaoe catalog/.test(e.message)) {
    await runXiaoeLogin(page);
    rows = await getXiaoeCatalog(page, url); // one retry after re-auth
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/801170c330a0ceba. Report an issue: GitHub.