{"record":{"id":"3623d84b2b72ddbb","repo":"jackwener/OpenCLI","slug":"pixiv-illustration-id-returned-malformed-detail","errorCode":null,"errorMessage":"Pixiv illustration ${id} returned malformed detail payload","messagePattern":"Pixiv illustration (.+?) returned malformed detail payload","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pixiv/detail.js","lineNumber":7,"sourceCode":"import { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';\nimport { pixivFetch } from './utils.js';\n\nfunction requireIllustBody(body, id) {\n    if (!body || Array.isArray(body) || typeof body !== 'object') {\n        throw new CommandExecutionError(`Pixiv illustration ${id} returned malformed detail payload`);\n    }\n    const illustId = String(body.illustId ?? '').trim();\n    const title = String(body.illustTitle ?? '').trim();\n    const userName = String(body.userName ?? '').trim();\n    const userId = String(body.userId ?? '').trim();\n    if (!/^\\d+$/.test(illustId) || illustId !== id || !title || !userName || !/^\\d+$/.test(userId)) {\n        throw new CommandExecutionError(`Pixiv illustration ${id} returned malformed detail payload`);\n    }\n    return { ...body, illustId, illustTitle: title, userName, userId };\n}\n\ncli({\n    site: 'pixiv',\n    name: 'detail',\n    access: 'read',\n    description: 'View illustration details (tags, stats, URLs)',\n    domain: 'www.pixiv.net',\n    strategy: Strategy.COOKIE,","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pixiv/detail.js#L1-L25","documentation":"requireIllustBody first checks the structural shape of the response from /ajax/illust/<id>: it must be a non-null, non-array object. If Pixiv returned null, an array, a string, or nothing at all (e.g. an error envelope or login page parsed to a non-object), this CommandExecutionError is thrown — this is the shape-check branch, distinct from the later content-check (error 3158).","triggerScenarios":"The /ajax/illust/<id> call resolves with body === null/undefined, a JSON array, or a string — e.g. Pixiv returned {\"error\":true} handled upstream to null, a deleted/restricted illustration returning an empty body, or a proxy/captcha HTML page.","commonSituations":"Illustration deleted or made private so the ajax endpoint returns an error object instead of detail data; session expired and Pixiv serves HTML/JSON that isn't the detail object; corporate proxy or Cloudflare challenge intercepting the request; rate limiting returning a non-standard envelope.","solutions":["Verify the illustration ID exists and is publicly viewable in a logged-in browser; use a different ID to confirm.","Re-authenticate the Pixiv session on `page` — expired logins commonly produce non-detail payloads.","Log/inspect the raw pixivFetch body when this fires to see whether it's an error envelope, HTML, or null.","Ensure pixivFetch's notFoundMsg/error handling covers the response before requireIllustBody runs.","Retry with backoff if Cloudflare/rate limiting is suspected; otherwise treat the artwork as unavailable."],"exampleFix":"// before\nconst body = await pixivFetch(page, `/ajax/illust/${id}`, { notFoundMsg: `Illustration not found: ${id}` });\nconst b = requireIllustBody(body, id);\n// after\nlet body = await pixivFetch(page, `/ajax/illust/${id}`, { notFoundMsg: `Illustration not found: ${id}` });\nif (body && typeof body === 'object' && body.error) {\n  throw new CommandExecutionError(`Pixiv illustration ${id} unavailable: ${body.message ?? 'unknown error'}`);\n}\nconst b = requireIllustBody(body, id);","handlingStrategy":"try-catch","validationCode":"function looksLikeIllustDetail(body) {\n  return body !== null && typeof body === 'object' && !Array.isArray(body);\n}\nif (!looksLikeIllustDetail(body)) {\n  console.warn('illust detail call returned non-object; session or artwork likely unavailable');\n}","typeGuard":"function isIllustDetailObject(body) {\n  return typeof body === 'object' && body !== null && !Array.isArray(body);\n}","tryCatchPattern":"let b;\ntry {\n  b = requireIllustBody(body, id);\n} catch (e) {\n  if (e.message.includes('malformed detail payload')) {\n    console.error(`Illust ${id} unavailable: got ${Array.isArray(body) ? 'array' : typeof body}; check login or artwork existence`);\n    return []; // skip gracefully\n  }\n  throw e;\n}","preventionTips":["Check the artwork exists and is public before fetching.","Keep the Pixiv page session logged in; expired sessions yield non-detail payloads.","Handle pixivFetch error envelopes (body.error) before calling requireIllustBody.","Log raw responses on failure to distinguish captcha/proxy interference from deleted artwork.","Add retries with backoff for transient rate-limit responses."],"tags":["pixiv","payload-validation","api-schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}