jackwener/OpenCLI · error · CommandExecutionError

failed

Error message

${actionLabel} failed: ${result?.error || 'invalid browser response'}

What it means

readHupuNextData waits up to ~5s for the page's `__NEXT_DATA__` script tag (optionally matching an expected tid) and parses it. If page.evaluate returns a falsy/non-object value, or `{ok:false, error}` (timeout, tid mismatch, or JSON.parse failure inside the browser), the helper throws CommandExecutionError with the browser-reported error, or the fallback 'invalid browser response' when result itself is unusable. It means Hupu's Next.js page data never became available or was not parseable.

Solutions

  1. Read the inner message (tid mismatch includes expected/actual tid and href) to see whether it was a timeout, tid mismatch, or missing data.
  2. Verify the tid/URL is a valid, existing thread and that the page loads in a normal browser.
  3. Increase options.timeoutMs if the network is slow; re-run the command.
  4. Log in to Hupu (or refresh cookies) if the site is serving an anti-bot or login page instead of thread data.
  5. If __NEXT_DATA__ is gone entirely, the site changed rendering — the CLI needs a parser update.

Example fix

// before
const data = await readHupuNextData(page, url, 'Read thread', { expectedTid: tid });
// after — give slow pages more time and validate tid first
if (!/^\d{9}$/.test(String(tid))) throw new Error(`invalid tid: ${tid}`);
const data = await readHupuNextData(page, url, 'Read thread', { expectedTid: String(tid), timeoutMs: 15000 });
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check the target thread before extraction
const res = await page.goto(threadUrl);
if (!res || !res.ok()) throw new Error(`thread page unreachable: ${threadUrl}`);
const hasNextData = await page.evaluate(() => Boolean(document.getElementById('__NEXT_DATA__')));
if (!hasNextData) console.warn('__NEXT_DATA__ not yet present; may need login or longer wait');

Type guard

function isNextDataResult(r) {
  return typeof r === 'object' && r !== null && 'ok' in r && (r.ok !== true || 'data' in r);
}

Try / catch

try {
  const data = await readHupuNextData(page, url, 'Read thread', { expectedTid, timeoutMs: 15000 });
} catch (err) {
  if (/tid不匹配/.test(err.message)) {
    const m = err.message.match(/expected=([^,]+), actual=([^,]+)/);
    console.error(`Redirected thread: wanted ${m?.[1]}, got ${m?.[2]}`);
  } else if (/无法找到帖子数据/.test(err.message)) {
    await refreshSession(); // likely a login/anti-bot wall
  }
  throw err;
}

Prevention

When it happens

Trigger: Any hupu read command backed by readHupuNextData when: the thread page never renders __NEXT_DATA__ within timeoutMs (default 5000), the rendered thread's tid differs from options.expectedTid (redirect/wrong tid), __NEXT_DATA__ is malformed JSON, or page.evaluate returns undefined/null so result?.error is undefined.

Common situations: Deleted or wrong thread tid causing a redirect to a different thread, Hupu serving a login/verification wall instead of the data page, slow network exceeding the 5s timeout, or Hupu changing its page framework so __NEXT_DATA__ no longer exists.

Related errors


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

Appendix: source

Thrown at clis/hupu/utils.js:97

        };
      }

      try {
        const text = document.getElementById('__NEXT_DATA__')?.textContent || '';
        return {
          ok: true,
          data: JSON.parse(text)
        };
      } catch (error) {
        return {
          ok: false,
          error: error instanceof Error ? error.message : String(error)
        };
      }
    })()
  `);
    if (!result || typeof result !== 'object' || !result.ok) {
        throw new CommandExecutionError(`${actionLabel} failed: ${result?.error || 'invalid browser response'}`);
    }
    return result.data;
}
export async function readHupuSearchData(page, url, actionLabel) {
    await page.goto(url);
    const result = await page.evaluate(`
    (async () => {
      const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
      const waitFor = async (predicate, timeoutMs = 5000) => {
        const start = Date.now();
        while (Date.now() - start < timeoutMs) {
          if (predicate()) return true;
          await wait(100);
        }
        return false;
      };

      const extractFromScript = () => {

View on GitHub (pinned to 49907e53dc)