jackwener/OpenCLI · error · CommandExecutionError
${label} returned a malformed API envelope
Error message
${label} returned a malformed API envelope What it means
Juejin's REST API wraps results in an envelope `{ err_no, err_msg, data }`. juejinFetch validates that the parsed JSON is a non-array object containing an `err_no` property and throws this CommandExecutionError when the shape doesn't match. It indicates a contract violation: the endpoint replied, but not with the documented Juejin envelope.
Source
Thrown at clis/juejin/utils.js:120
);
}
if (resp.status === 429) {
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;View on GitHub (pinned to 49907e53dc)
Solutions
- Log the parsed body to see the actual shape and compare it with the expected `{ err_no, err_msg, data }` envelope.
- Update the adapter if Juejin renamed/removed `err_no` — check the calling command's endpoint against current Juejin API docs.
- Confirm you are hitting the intended path (a wrong path on the same host can return a different envelope with 200).
- Upgrade the opencli juejin adapter if a newer version adapts to the changed schema.
- Report/pin the endpoint: if a gateway is substituting its own JSON error, fix the proxy config instead.
Example fix
// before (adapter assumes envelope blindly)
const payload = await juejinFetch(path, body, label);
// after (caller detects envelope drift defensively)
const payload = await juejinFetch(path, body, label); // throws on bad envelope
if (!('data' in payload)) throw new Error(`unexpected envelope: ${JSON.stringify(payload).slice(0, 200)}`); Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
function isJuejinEnvelope(payload) {
return (
payload !== null &&
typeof payload === 'object' &&
!Array.isArray(payload) &&
Object.hasOwn(payload, 'err_no') &&
typeof payload.err_no === 'number'
);
} Try / catch
try {
const payload = await juejinFetch(path, body, label); // envelope already validated internally
if (!Array.isArray(payload.data)) throw new Error('envelope ok but data missing');
} catch (err) {
if (err instanceof CommandExecutionError && /malformed API envelope/.test(err.message)) {
console.error(`Juejin API contract changed or a gateway answered: ${err.message}. Update/inspect the adapter.`);
return;
}
throw err;
} Prevention
- Treat this error as schema drift: immediately dump the raw response shape and compare to `{ err_no, err_msg, data }`.
- Keep the adapter updated; subscribe to Juejin API changes before running long-lived jobs.
- Verify the exact endpoint path — a wrong path on the same host can return a 200 with a foreign envelope.
- Add an integration test that asserts the envelope shape so drift is caught before production runs.
When it happens
Trigger: The response parsed as JSON but is null, an array, a scalar, or an object without `err_no` — e.g. the endpoint changed its schema, a gateway returned its own JSON error object ( `{message: ...}` ), or the request hit an unexpected Juejin route returning a different envelope.
Common situations: Juejin silently changing/redesigning an endpoint's response shape (adapter version drift); a CDN or gateway returning its own JSON `{error: ...}` with 200; hitting a path that belongs to a different Juejin API family with a different envelope; an old cached adapter against a new backend.
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
- Gmail ${label} returned a malformed Browser Bridge envelope
- Suno feed lookup returned malformed clips payload
- Stale .git/index.lock found — remove it first
- archive search returned malformed payload: result row is mis
- archive snapshots returned malformed CDX payload: missing "$
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/b5cf2e6bf7574e56.
Report an issue: GitHub.