{"record":{"id":"0336e3b90243ddd0","repo":"jackwener/OpenCLI","slug":"pixiv-user-profile-returned-malformed-novel-id","errorCode":null,"errorMessage":"Pixiv user profile returned malformed novel ID: ${invalid}","messagePattern":"Pixiv user profile returned malformed novel ID: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pixiv/novels.js","lineNumber":49,"sourceCode":"    novel_id: id,\n    words: optionalCount(item.wordCount, 'word count'),\n    characters: optionalCount(item.textCount ?? item.characterCount, 'character count'),\n    bookmarks: optionalCount(item.bookmarkCount, 'bookmark count', 0),\n    tags: tagsToString(item.tags),\n    created: dateOnly(item.createDate),\n    url: `https://www.pixiv.net/novel/show.php?id=${id}`,\n  };\n}\n\nfunction requireProfileNovelIds(body) {\n  const payload = requirePixivPayloadObject(body, 'Pixiv user profile');\n  if (!payload.novels || Array.isArray(payload.novels) || typeof payload.novels !== 'object') {\n    throw new CommandExecutionError('Pixiv user profile returned malformed novels payload');\n  }\n  const ids = Object.keys(payload.novels);\n  const invalid = ids.find(id => !/^\\d+$/.test(id));\n  if (invalid) {\n    throw new CommandExecutionError(`Pixiv user profile returned malformed novel ID: ${invalid}`);\n  }\n  return ids;\n}\n\nfunction requireDetailWorks(body) {\n  const payload = requirePixivPayloadObject(body, 'Pixiv user novel details');\n  if (!payload.works || Array.isArray(payload.works) || typeof payload.works !== 'object') {\n    throw new CommandExecutionError('Pixiv user novel details returned malformed works payload');\n  }\n  return payload.works;\n}\n\ncli({\n  site: 'pixiv',\n  name: 'novels',\n  access: 'read',\n  description: \"List a Pixiv user's novels\",\n  domain: 'www.pixiv.net',","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pixiv/novels.js#L31-L67","documentation":"After requireProfileNovelIds obtains the novels map, every key must be a numeric string (Pixiv novel ID). If any key fails /^\\d+$/, the library throws naming the invalid key, because non-numeric keys (e.g. 'meta', 'next', or error fields embedded in the map) cannot be novel IDs and would produce broken detail requests downstream.","triggerScenarios":"The profile novels object contains non-ID keys — e.g. the API nests pagination/metadata fields alongside numeric IDs, keys arrive with quotes/whitespace/prefixes ('novel_123'), or a mocked/local fixture uses string slugs instead of numeric IDs.","commonSituations":"Pixiv API schema drift adding metadata keys to the novels map; hand-crafted test fixtures or cached snapshots with slug keys; a proxy rewriting keys; copying payload from docs with placeholder keys.","solutions":["Log Object.keys(payload.novels) and inspect which key is non-numeric","Filter out known non-numeric/metadata keys before passing the payload to the CLI","Normalize keys by stripping non-digit prefixes (e.g. key.replace(/^\\D+/, '')) if the API changed key format","Refresh fixtures/cache with a fresh API response reflecting the current schema"],"exampleFix":"// before\nconst ids = requireProfileNovelIds(body);\n// after\nconst cleaned = { ...body, novels: Object.fromEntries(\n  Object.entries(body.novels).filter(([k]) => /^\\d+$/.test(k))\n)};\nconst ids = requireProfileNovelIds(cleaned);","handlingStrategy":"validation","validationCode":"const ids = Object.keys(body?.novels ?? {});\nconst invalid = ids.filter(id => !/^\\d+$/.test(id));\nif (invalid.length) {\n  console.warn('Ignoring non-numeric novel keys:', invalid);\n}\nconst validIds = ids.filter(id => /^\\d+$/.test(id));","typeGuard":"function isNumericIdKey(k) {\n  return /^\\d+$/.test(k);\n}\nfunction numericIdKeys(novels) {\n  return Object.keys(novels).filter(isNumericIdKey);\n}","tryCatchPattern":"try {\n  ids = requireProfileNovelIds(body);\n} catch (err) {\n  const m = String(err.message).match(/malformed novel ID: (.+)$/);\n  if (m) {\n    console.warn(`Filtering out invalid novel key \"${m[1]}\"`);\n    ids = Object.keys(body.novels).filter(k => /^\\d+$/.test(k));\n  } else throw err;\n}","preventionTips":["Filter object keys against /^\\d+$/ before consuming API maps","Normalize key formats (strip prefixes like 'novel_') if the API changes them","Keep fixtures current so metadata keys added by Pixiv are caught in tests","Log rejected keys instead of silently discarding them"],"tags":["pixiv","cli","validation","id-format"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}