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
- 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).
- 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.
- Reset the cursor to '0' or omit it to restart pagination from the beginning of the feed.
- Check whether the target article still exists by opening https://juejin.cn/post/<id> in a browser; skip deleted/private items.
- 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
- Always pass cursors through requireCursor and categories through resolveCategory before calling the API.
- Verify article/category ids still resolve by loading the juejin.cn URL before relying on them.
- Retry once on transient-looking codes; log err_no values you cannot explain and report contract changes.
- Keep the adapter's endpoint paths in sync with the live Juejin API.
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
- ${label} returned HTTP ${resp.status}: ${summarizeApiError(p
- archive search failed: HTTP ${resp.status}
- HTTP ${result.httpStatus} from /api/organizations
- coingecko derivatives returned HTTP ${resp.status}
- ${label} returned HTTP ${res.status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/effaf99c3089a3d2.
Report an issue: GitHub.