jackwener/OpenCLI · error · CommandExecutionError

${label} returned err_no ${payload.err_no}: ${payload.err_ms

Error message

${label} returned err_no ${payload.err_no}: ${payload.err_msg ?? ''}

What it means

juejinFetch in clis/juejin/utils.js:123 validates the Juejin API envelope `{ err_no, err_msg, data }`. When the HTTP response is valid JSON with a proper envelope but `err_no` is non-zero, the library surfaces the API-level business error as a CommandExecutionError including the numeric code and err_msg. This is the Juejin server rejecting the request (bad parameters, restricted content, etc.), not a transport failure.

Source

Thrown at clis/juejin/utils.js:123

        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'Juejin throttles bursty traffic; wait a few seconds and retry.',
        );
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    let payload;
    try {
        payload = await resp.json();
    } catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'err_no')) {
        throw new CommandExecutionError(`${label} returned a malformed API envelope`);
    }
    if (payload.err_no !== 0) {
        throw new CommandExecutionError(`${label} returned err_no ${payload.err_no}: ${payload.err_msg ?? ''}`);
    }
    return payload;
}

export function readDataArray(payload, label) {
    if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'data')) {
        throw new CommandExecutionError(`${label} returned a malformed payload`);
    }
    if (!Array.isArray(payload.data)) {
        throw new CommandExecutionError(`${label} returned a non-array data field`);
    }
    if (payload.data.length === 0) {
        throw new EmptyResultError(label, `${label} returned no articles.`);
    }
    return payload.data;
}

function readArticleId(value, label) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the err_msg in the error message — it states the API's own reason; fix the request parameter it points to (cursor, category id, article id).
  2. Verify the category id or slug is one of the supported aliases in CATEGORY_ALIASES (backend, frontend, android, ios, ai) or a currently valid numeric id.
  3. Reset the cursor to '0' or omit it to restart pagination from the beginning of the feed.
  4. Check whether the target article still exists by opening https://juejin.cn/post/<id> in a browser; skip deleted/private items.
  5. Retry after a short delay in case the code indicates a transient server-side condition; if persistent, report the changed API contract upstream.

Example fix

// before: cursor past end of feed
const items = await listArticles({ cursor: '9999999' });
// after: start from the default cursor and page forward
const items = await listArticles({ cursor: '0' });
Defensive patterns

Strategy: try-catch

Validate before calling

// Cannot pre-validate server-side err_no; validate inputs you control
cursor = requireCursor(rawCursor);          // non-negative decimal integer
categoryId = resolveCategory(rawCategory);  // known alias or 16-20 digit id

Type guard

function isOkEnvelope(p) {
  return p !== null && typeof p === 'object' && !Array.isArray(p)
    && Object.hasOwn(p, 'err_no') && p.err_no === 0;
}

Try / catch

try {
  const payload = await juejinFetch(path, body, 'juejin list');
  // ...
} catch (err) {
  if (err instanceof CommandExecutionError && /err_no \d+/.test(err.message)) {
    const code = Number(err.message.match(/err_no (\d+)/)?.[1]);
    console.error(`Juejin API rejected the request (err_no=${code}); check your cursor/category/article id.`);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Any call path through juejinFetch (e.g. recommend feed, hot list, category article queries) where api.juejin.cn responds HTTP 200 with a JSON body whose err_no !== 0, such as an invalid cursor, unknown category id, deleted/private article id, or endpoint parameter the API refuses.

Common situations: Querying a category with a raw id that no longer exists; passing a cursor beyond the end of the feed; requesting an article that was deleted or made private; Juejin changing the endpoint contract so previously valid params now return an error code; hitting internal API endpoints that now require auth.

Related errors


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