jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu creator-note-detail: signed API

Error message

xiaohongshu creator-note-detail: signed API ${suffix} returned HTTP ${capture.status ?? 'non-2xx'}

What it means

This CommandExecutionError is thrown by parseCapturedJson in clis/xiaohongshu/creator-note-detail.js when a captured signed datacenter/note API response reports capture.ok !== true, i.e. the dashboard's own fetch/XHR completed with a non-2xx HTTP status (or the status is unknown). The library wraps the browser's fetch/XHR to harvest responses the page itself makes, and refuses to hand back payloads from failed HTTP responses.

Solutions

  1. Log into creator.xiaohongshu.com in the connected Chrome profile and refresh the session cookies, then retry.
  2. Verify the note-id exists and belongs to the logged-in account (a wrong note_id yields 404).
  3. Wait and retry — risk control (406-style blocks) is often transient; slow down polling/retries between runs.
  4. Check the HTTP status embedded in the error to route: 401/403 → re-login, 404 → wrong note_id, 5xx → retry later.
  5. Update the CLI if Xiaohongshu changed the datacenter API, so endpoint suffixes match current endpoints.

Example fix

// before
const rows = await run('xiaohongshu creator-note-detail', { 'note-id': id });
// after
try {
  const rows = await run('xiaohongshu creator-note-detail', { 'note-id': id });
} catch (e) {
  if (/HTTP 40[13]/.test(e.message)) await refreshCreatorLogin();
  else if (/HTTP 404/.test(e.message)) throw new Error(`note ${id} not found`);
  else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const noteId = (id) => /^\d+$/.test(String(id)) && isLoggedInToCreatorXhs();
if (!noteId(args['note-id'])) throw new Error('need valid note-id and creator.xiaohongshu.com login');

Type guard

function isOkCapture(c) {
  return typeof c === 'object' && c !== null && c.ok === true && typeof c.body === 'string';
}

Try / catch

try {
  const rows = await run('xiaohongshu creator-note-detail', { 'note-id': id });
} catch (e) {
  if (/returned HTTP (401|403)/.test(e.message)) await reloginCreatorXhs();
  else if (/returned HTTP 404/.test(e.message)) throw new Error(`note ${id} not found`);
  throw e;
}

Prevention

When it happens

Trigger: A signed /datacenter/note/* endpoint called during the SPA navigation to /statistics/note-detail returns HTTP 401/403/404/406/5xx, so window.__xhsCapture[url] = { status, ok: false, body } is stored and parseCapturedJson rejects it. Note that a direct page-side fetch bypassing x-s signing gets 406, but here the page's own signed call failed.

Common situations: Expired or missing creator.xiaohongshu.com login cookies (401/403); wrong or deleted note_id (404); risk-control/anti-bot interception (406/461); transient 5xx from Xiaohongshu; the dashboard API surface changed and the suffix now maps to a different failing endpoint.

Related errors


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

Appendix: source

Thrown at clis/xiaohongshu/creator-note-detail.js:306

        assertOptionalPlainObject(payload, 'day', suffix);
    }
    if (endpoint.key === 'audienceSource') {
        assertOptionalArray(payload, 'source', suffix);
    }
    if (endpoint.key === 'audienceSourceDetail') {
        for (const key of ['gender', 'age', 'city', 'interest']) {
            assertOptionalArray(payload, key, suffix);
        }
    }
    return payload;
}
function parseCapturedJson(capture, endpoint) {
    const suffix = endpoint.suffix;
    if (!capture || typeof capture !== 'object') {
        throw new CommandExecutionError(`xiaohongshu creator-note-detail: malformed capture for ${suffix}`);
    }
    if (capture.ok !== true) {
        throw new CommandExecutionError(`xiaohongshu creator-note-detail: signed API ${suffix} returned HTTP ${capture.status ?? 'non-2xx'}`);
    }
    if (typeof capture.body !== 'string') {
        throw new CommandExecutionError(`xiaohongshu creator-note-detail: signed API ${suffix} returned a non-text body`);
    }
    try {
        const envelope = JSON.parse(capture.body);
        const payload = isPlainObject(envelope) && Object.hasOwn(envelope, 'data') ? envelope.data : envelope;
        return validateCapturedPayload(payload, endpoint);
    }
    catch {
        throw new CommandExecutionError(`xiaohongshu creator-note-detail: signed API ${suffix} returned invalid JSON or payload shape`);
    }
}
// Capture the dashboard's signed datacenter/note responses on window.__xhsCapture
// since a direct fetch() from page.evaluate bypasses the x-s signing and gets 406.
async function installXhsFetchCaptureHook(page) {
    await page.evaluate(`(() => {
    const targetPaths = ${JSON.stringify(DETAIL_API_ENDPOINTS.map((endpoint) => endpoint.suffix))};

View on GitHub (pinned to 49907e53dc)