jackwener/OpenCLI · error · CommandExecutionError

Pixiv novel ${id} returned malformed detail payload

Error message

Pixiv novel ${id} returned malformed detail payload

What it means

requireNovelDownloadBody checks the novel detail response before any files are written. If the body is null, an array, or not a plain object, it throws this error meaning the /ajax/novel/{id} endpoint did not return the expected detail object.

Source

Thrown at clis/pixiv/novel-download-utils.js:17

import * as fs from 'node:fs';
import * as path from 'node:path';
import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';
import { pixivFetch } from './utils.js';
import { dateOnly, tagsToString } from './bookmark-utils.js';

function optionalDownloadCount(value, label) {
  if (value == null || value === '') return null;
  if (!Number.isSafeInteger(value) || value < 0) {
    throw new CommandExecutionError(`Pixiv novel ${label} returned malformed data`);
  }
  return value;
}

function requireNovelDownloadBody(body, id) {
  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 = typeof body.title === 'string' ? body.title.trim() : '';
  const author = typeof body.userName === 'string' ? body.userName.trim() : '';
  const userId = String(body.userId ?? '').trim();
  if (typeof body.content !== 'string') {
    throw new CommandExecutionError(`Pixiv novel ${id} returned malformed content payload`);
  }
  if (!/^\d+$/.test(novelId) || novelId !== id || !title || !author || !/^\d+$/.test(userId)) {
    throw new CommandExecutionError(`Pixiv novel ${id} returned malformed detail payload`);
  }
  // Validate metadata before any file is planned or created.
  tagsToString(body.tags);
  const createdDate = dateOnly(body.createDate);
  const wordCount = optionalDownloadCount(body.wordCount, 'word count');
  const bookmarkCount = optionalDownloadCount(body.bookmarkCount, 'bookmark count');
  return {
    ...body,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the novel ID exists by opening it on pixiv in a browser
  2. Refresh pixiv cookies / re-authenticate the session
  3. Slow down bulk downloads to avoid throttling
  4. Log the raw response to identify whether pixiv changed the payload shape
Defensive patterns

Strategy: type-guard

Validate before calling

const body = await pixivFetch(page, `/ajax/novel/${id}`);
if (!body || Array.isArray(body) || typeof body !== 'object') {
  throw new Error(`Novel ${id}: non-object detail payload`);
}

Type guard

const isNovelDetail = (v) => !!v && !Array.isArray(v) && typeof v === 'object';

Try / catch

try {
  await novelDownload(id);
} catch (e) {
  if (/malformed detail payload/.test(e.message)) {
    // refresh session, back off, or skip this ID
  } else throw e;
}

Prevention

When it happens

Trigger: pixivFetch returns null/falsey body, an array, or a non-object (error JSON, HTML parsed unexpectedly, or rate-limit page) for the novel detail endpoint.

Common situations: Novel deleted or ID wrong in a way that bypasses notFoundMsg; session expired causing a redirect payload; pixiv rate limiting during bulk novel downloads.

Understand the failure class

Related errors


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