jackwener/OpenCLI · error · CommandExecutionError

Pixiv bookmarks returned malformed payload

Error message

Pixiv bookmarks returned malformed payload

What it means

normalizeBookmarkWorks validates the JSON returned by Pixiv's /ajax/user/<id>/.../bookmarks endpoint before rows are built. It accepts a plain array, an object with a `works` array, or an object with an object-valued `works` map; anything else is a payload shape the library cannot safely iterate, so it throws this CommandExecutionError to surface upstream/API drift instead of producing garbage rows.

Source

Thrown at clis/pixiv/bookmark-utils.js:55

      throw new CommandExecutionError('Pixiv item returned malformed tag');
    }
    return value.trim();
  }).join(', ');
}

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

export function normalizeBookmarkWorks(body) {
  if (Array.isArray(body)) return body;
  if (Array.isArray(body?.works)) return body.works;
  if (body?.works && typeof body.works === 'object') return Object.values(body.works);
  throw new CommandExecutionError('Pixiv bookmarks returned malformed payload');
}

export function bookmarkRow(work, index, type, bookmarkOwnerId) {
  const item = requirePixivPayloadObject(work, 'Pixiv bookmark item');
  const isNovel = type === 'novel';
  const id = requirePixivId(item.id ?? (isNovel ? item.novelId : item.illustId), 'Pixiv bookmark item');
  const title = requirePixivString(item.title ?? item.illustTitle, 'Pixiv bookmark item');
  const author = requirePixivString(item.userName ?? item.user_name, 'Pixiv bookmark item');
  const userId = requirePixivId(item.userId ?? item.user_id, 'Pixiv bookmark item');
  return {
    rank: index + 1,
    type,
    bookmark_owner_id: bookmarkOwnerId,
    title,
    author,
    user_id: userId,
    illust_id: isNovel ? '' : id,
    novel_id: isNovel ? id : '',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check Pixiv login state: re-authenticate the page/session, since an unauthenticated ajax response is the most common cause of a non-works payload.
  2. Log the raw body before calling normalizeBookmarkWorks (or catch and inspect body) to see the actual schema returned.
  3. Update the normalizer to handle the new response shape (e.g. accept body.bookmarks or body.body.works) if Pixiv changed its schema.
  4. Retry after a delay if the payload was a rate-limit/error envelope; use pixivFetch's error handling / notFoundMsg paths.
  5. Pin/verify the library version matches the current Pixiv API behavior; upgrade if a fix exists.

Example fix

// before
const body = await pixivFetch(page, path, {...});
const works = normalizeBookmarkWorks(body);
// after
const body = await pixivFetch(page, path, {...});
let works;
try {
  works = normalizeBookmarkWorks(body);
} catch (e) {
  if (!body?.error) throw e;
  throw new CommandExecutionError(`Pixiv bookmarks request failed: ${body.message ?? 'unauthenticated or rate-limited'}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

function isUsableBookmarksBody(body) {
  return Array.isArray(body) ||
    Array.isArray(body?.works) ||
    (body?.works && typeof body.works === 'object');
}
if (!isUsableBookmarksBody(body)) {
  console.error('Unexpected bookmarks payload:', JSON.stringify(body).slice(0, 500));
}

Type guard

function isUsableBookmarksBody(body) {
  return Array.isArray(body) ||
    (body !== null && typeof body === 'object' &&
      (Array.isArray(body.works) || (body.works !== null && typeof body.works === 'object')));
}

Try / catch

try {
  const works = normalizeBookmarkWorks(body);
  // ...build rows
} catch (e) {
  if (e.message.includes('malformed payload')) {
    // inspect/log raw body, check login state, then rethrow or degrade gracefully
    throw new Error(`Bookmarks unavailable (payload shape: ${typeof body}); check Pixiv login/session`, { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Pixiv's ajax endpoint returns an HTML login page, an error JSON like {"error":true,...}, an empty body, or a new schema where bookmarks live under a different key — any body that is neither an array nor an object with a usable `works` field.

Common situations: Expired or missing Pixiv login session so the ajax call returns a redirect/login page; Pixiv A/B-testing a new bookmark response schema; rate-limit or error envelope JSON returned with HTTP 200; scraping a region where the endpoint is gated.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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