jackwener/OpenCLI · error · CommandExecutionError
Pixiv novel returned malformed series metadata
Error message
Pixiv novel returned malformed series metadata
What it means
novelRowFromBody validates the Pixiv /ajax/novel response before building a row. If seriesNavData is present but is an array or a non-object primitive (string, number, boolean), the payload's series metadata is structurally invalid, so the CLI throws CommandExecutionError instead of emitting bad data. This guards against upstream Pixiv API changes or proxy-injected garbage.
Source
Thrown at clis/pixiv/novel.js:33
if (!body || Array.isArray(body) || typeof body !== 'object') {
throw new CommandExecutionError(`Pixiv novel ${id} returned malformed detail payload`);
}
const novelId = String(body.id ?? '').trim();
const title = String(body.title ?? '').trim();
const userName = String(body.userName ?? '').trim();
const userId = String(body.userId ?? '').trim();
if (!/^\d+$/.test(novelId) || novelId !== id || !title || !userName || !/^\d+$/.test(userId)) {
throw new CommandExecutionError(`Pixiv novel ${id} returned malformed detail payload`);
}
return { payload: body, identity: { novelId, title, userName, userId } };
}
export function novelRowFromBody(body, id) {
const normalized = requireNovelBody(body, id);
const b = normalized.payload;
const identity = normalized.identity;
if (b.seriesNavData != null && (Array.isArray(b.seriesNavData) || typeof b.seriesNavData !== 'object')) {
throw new CommandExecutionError('Pixiv novel returned malformed series metadata');
}
const series = b.seriesNavData || {};
const seriesId = b.seriesId ?? series.seriesId ?? '';
const seriesTitle = b.seriesTitle ?? series.title ?? '';
if (seriesId !== '' && !/^\d+$/.test(String(seriesId))) {
throw new CommandExecutionError('Pixiv novel returned malformed series ID');
}
if (seriesTitle !== '' && typeof seriesTitle !== 'string') {
throw new CommandExecutionError('Pixiv novel returned malformed series title');
}
const seriesOrder = series.order ?? b.seriesContentOrder ?? '';
if (seriesOrder !== '' && (!Number.isSafeInteger(seriesOrder) || seriesOrder < 1)) {
throw new CommandExecutionError('Pixiv novel returned malformed series order');
}
return {
novel_id: identity.novelId,
title: identity.title,
author: identity.userName,View on GitHub (pinned to 49907e53dc)
Solutions
- Log the raw response body from pixivFetch to confirm what shape seriesNavData actually has
- Update the CLI to the latest version so the parser matches the current Pixiv API schema
- If a proxy/VPN is involved, retry the request with an authenticated session or different egress so Pixiv returns real JSON
- As a caller, strip or null-out seriesNavData before invoking novelRowFromBody when you do not need series info
Example fix
// before (caller passing raw/legacy body) const row = novelRowFromBody(legacyBody, id); // after if (Array.isArray(legacyBody.seriesNavData)) legacyBody.seriesNavData = null; const row = novelRowFromBody(legacyBody, id);
Defensive patterns
Strategy: type-guard
Validate before calling
function hasValidSeriesMeta(body) {
const s = body?.seriesNavData;
return s == null || (typeof s === 'object' && !Array.isArray(s));
}
if (!hasValidSeriesMeta(body)) throw new Error('unexpected seriesNavData shape'); Type guard
function isSeriesNavData(v) {
return v == null || (typeof v === 'object' && !Array.isArray(v));
} Try / catch
try {
const row = novelRowFromBody(body, id);
} catch (e) {
if (e.message.includes('malformed series metadata')) {
console.warn('seriesNavData not an object; proceeding without series info');
} else throw e;
} Prevention
- Never feed raw/legacy API bodies straight into novelRowFromBody without checking seriesNavData's type
- Pin and update the CLI version when Pixiv schema changes are announced
- Null-out seriesNavData in callers that do not need series data
When it happens
Trigger: The Pixiv AJAX novel endpoint returns seriesNavData as an array, a string, a number, or a boolean instead of a null/object (e.g. API schema change, HTML-interstitial or error JSON from a proxy/blocked request, cached/legacy payload shape).
Common situations: Pixiv changes the /ajax/novel/{id} response schema; a scraping proxy or auth wall returns an unexpected body; unit fixtures built from an old API version carry a seriesNavData array; callers pass a hand-crafted body into novelRowFromBody.
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
- Pixiv novel returned malformed series ID
- Bilibili ${label} API returned malformed top_replies
- Bilibili creator comparison returned malformed stat data for
- Bilibili view API returned malformed paid-content metadata
- Nowcoder returned a malformed ${label}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/307d11960a8d73e5.
Report an issue: GitHub.