jackwener/OpenCLI · error · CommandExecutionError
juejin recommend returned a malformed cursor
Error message
juejin recommend returned a malformed cursor
What it means
The Juejin recommend CLI wraps its pagination cursor parser (requireCursor) in a try/catch inside readResponseCursor. When the API response contains a cursor value that cannot be parsed as a valid non-negative decimal integer cursor, the underlying ArgumentError is replaced with a CommandExecutionError indicating the remote service returned malformed data. This signals an unexpected API response shape rather than a caller mistake.
Source
Thrown at clis/juejin/recommend.js:21
// Hits the `recommend_all_feed` endpoint, which mirrors what the Juejin web UI
// renders on the front page; `sort_type` 200 is the default "recommended" mix.
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CommandExecutionError } from '@jackwener/opencli/errors';
import {
juejinFetch,
mapFeedItem,
readDataArray,
requireBoundedInt,
requireCursor,
} from './utils.js';
function readResponseCursor(value) {
if (value == null || value === '') return '';
try {
return requireCursor(value);
}
catch {
throw new CommandExecutionError('juejin recommend returned a malformed cursor');
}
}
cli({
site: 'juejin',
name: 'recommend',
access: 'read',
description: 'Juejin (掘金) homepage recommended article feed',
domain: 'api.juejin.cn',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'limit', type: 'int', default: 20, help: 'Max articles (1-100, single page).' },
{ name: 'cursor', type: 'string', default: '0', help: 'Pagination cursor; pass back the previous response\'s next-page cursor to keep scrolling.' },
],
columns: ['rank', 'article_id', 'title', 'brief', 'views', 'likes', 'comments', 'author', 'tags', 'url', 'next_cursor', 'has_more'],
func: async (args) => {
const limit = requireBoundedInt(args.limit, 20, 100);View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the command once — transient CDN/proxy corruption can produce a bad payload; a fresh request often returns a valid cursor.
- Check whether the juejin API changed its cursor format; update the CLI's requireCursor regex/parser to accept the new format.
- Pass an explicit --cursor argument with a known-good numeric cursor from a previous successful response to bypass the malformed server cursor.
- Update the CLI package if a newer version adapts to the new API response shape.
- Report/inspect the raw payload (log payload.cursor) to confirm what the server actually returned before filing a bug.
Example fix
// before (strict numeric-only cursor parsing) if (typeof raw === 'string' && /^(0|[1-9]\d*)$/.test(raw)) return raw; // after (accept opaque server cursors) if (typeof raw === 'string' && /^[A-Za-z0-9_-]+$/.test(raw)) return raw;
Defensive patterns
Strategy: try-catch
Validate before calling
function isPlausibleCursor(v){ return v == null || v === '' || (typeof v === 'string' && /^[A-Za-z0-9]+$/.test(v)) || (typeof v === 'number' && Number.isSafeInteger(v) && v >= 0); } Type guard
function isUsableCursor(v){ return (typeof v === 'string' && /^(0|[1-9]\d*)$/.test(v)) || (typeof v === 'number' && Number.isSafeInteger(v) && v >= 0) || v == null || v === ''; } Try / catch
try {
const page = cliRecommend({ limit: 20, cursor });
} catch (e) {
if (/malformed cursor/.test(e.message)) {
cursor = '0'; // restart pagination from the beginning
const page = cliRecommend({ limit: 20, cursor });
} else throw e;
} Prevention
- Always feed next_cursor from a previous response back verbatim instead of constructing cursors yourself.
- Validate payload.cursor shape before passing it onward in pipelines.
- Pin the CLI version and watch for API format changes; test pagination against a recorded fixture.
- Wrap pagination loops to reset to cursor '0' on malformed-cursor errors to avoid hard failures.
When it happens
Trigger: Calling the juejin recommend command when the upstream API returns a payload whose cursor field is a non-empty value that fails requireCursor validation, e.g. a string with non-digit characters, a negative number, a float, an object, or a boolean.
Common situations: Juejin changes its cursor format (e.g. switches to opaque base64 tokens or signed cursors); a proxy or cache returns an HTML error page parsed as JSON with a string cursor; scraping/CDN interposition alters response fields; a mocked or recorded fixture uses a placeholder cursor value like '<cursor>' or 'abc'.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- juejin recommend returned has_more without a next cursor
- juejin recommend returned a malformed has_more flag
- Xiaoyuzhou history returned an invalid loadMoreKey
- ${label} must be a positive integer
- ${label} must be <= ${maxValue}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/90196c3638386720.
Report an issue: GitHub.