jackwener/OpenCLI · error · CommandExecutionError

Pixiv user novels returned mismatched novel detail payload f

Error message

Pixiv user novels returned mismatched novel detail payload for ${expectedId}

What it means

userNovelRow fetches/validates a novel detail item that is expected to correspond to a specific novel ID (expectedId). If requirePixivId(item.id) parses successfully but does not equal expectedId, the library throws because Pixiv returned the detail payload for a different novel than requested — continuing would attribute the wrong title/content to the ranked row. This is an integrity check on positional/identity alignment between the profile novel list and the detail responses.

Source

Thrown at clis/pixiv/novels.js:25

  requirePixivId,
  requirePixivPayloadObject,
  requirePixivString,
} from './utils.js';
import { dateOnly, tagsToString } from './bookmark-utils.js';

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

function userNovelRow(work, rank, expectedId) {
  const item = requirePixivPayloadObject(work, 'Pixiv user novel item');
  const id = requirePixivId(item.id, 'Pixiv user novel item');
  if (id !== expectedId) {
    throw new CommandExecutionError(`Pixiv user novels returned mismatched novel detail payload for ${expectedId}`);
  }
  const title = requirePixivString(item.title, 'Pixiv user novel item');
  return {
    rank,
    title,
    novel_id: id,
    words: optionalCount(item.wordCount, 'word count'),
    characters: optionalCount(item.textCount ?? item.characterCount, 'character count'),
    bookmarks: optionalCount(item.bookmarkCount, 'bookmark count', 0),
    tags: tagsToString(item.tags),
    created: dateOnly(item.createDate),
    url: `https://www.pixiv.net/novel/show.php?id=${id}`,
  };
}

function requireProfileNovelIds(body) {
  const payload = requirePixivPayloadObject(body, 'Pixiv user profile');
  if (!payload.novels || Array.isArray(payload.novels) || typeof payload.novels !== 'object') {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Print both expectedId and the received id from the payload to confirm which novel mismatched
  2. Re-fetch the profile novel ID list immediately before requesting details to avoid stale/deleted IDs
  3. Pair detail payloads by their returned id (build a map id->item) instead of assuming positional correspondence
  4. Clear any HTTP cache/proxy between you and the Pixiv API
  5. If novel is genuinely gone, skip that rank/id instead of throwing

Example fix

// before
rows = ids.map((id, i) => userNovelRow(works[i], i + 1, id));
// after
const byId = new Map(works.map(w => [String(w.id), w]));
rows = ids.flatMap((id, i) =>
  byId.has(String(id)) ? [userNovelRow(byId.get(String(id)), i + 1, id)] : []
);
Defensive patterns

Strategy: try-catch

Validate before calling

const detail = await fetchNovelDetail(id);
if (detail && String(detail.id) !== String(id)) {
  console.warn(`Detail payload id ${detail.id} does not match requested ${id}; skipping`);
}

Type guard

function matchesExpectedId(item, expectedId) {
  return item != null && String(item.id) === String(expectedId);
}

Try / catch

try {
  rows.push(userNovelRow(work, rank, expectedId));
} catch (err) {
  if (String(err.message).includes('mismatched novel detail payload')) {
    console.warn(`Skipping ${expectedId}: detail payload mismatch`);
    continue;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the user-novels CLI when the detail payload order or content no longer matches the profile's novels map keys: e.g. the API returns works keyed/ordered differently than requested, a novel was deleted between listing and detail fetch, or the caller passes a mismatched expectedId to userNovelRow.

Common situations: Pixiv returning stale or reordered data (profile updated mid-run); caching layer mixing payloads across users; off-by-one in the caller's loop pairing rank/id with detail items; a novel made private/deleted after the ID list was obtained.

Related errors


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