{"record":{"id":"4c713119cc9815ce","repo":"jackwener/OpenCLI","slug":"pixiv-image-download-returned-unexpected-content-t","errorCode":null,"errorMessage":"Pixiv image download returned unexpected content type for ${file.filename}","messagePattern":"Pixiv image download returned unexpected content type for (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/pixiv/bookmark-download.js","lineNumber":88,"sourceCode":"  const finalPath = path.join(outputRoot, 'illust', row.illust_id);\n  if (pixivPathEntryExists(finalPath)) {\n    throw new CommandExecutionError(`Refusing to overwrite existing Pixiv download: ${finalPath}`);\n  }\n  const createdDirs = [];\n  for (let cursor = path.dirname(finalPath); !fs.existsSync(cursor); cursor = path.dirname(cursor)) {\n    createdDirs.push(cursor);\n    if (path.dirname(cursor) === cursor) break;\n  }\n  return { kind: 'illust', illustId: row.illust_id, finalPath, files, createdDirs };\n}\n\nfunction validateImageDownload(result, file) {\n  if (!result || typeof result !== 'object' || result.success !== true || !Number.isSafeInteger(result.size) || result.size <= 0) {\n    throw new CommandExecutionError(`Pixiv image download failed: ${result?.error || 'invalid download result'}`);\n  }\n  const final = parsePixivImageUrl(result.finalUrl, 'Pixiv image download');\n  if (final.contentType !== file.contentType || result.contentType !== file.contentType) {\n    throw new CommandExecutionError(`Pixiv image download returned unexpected content type for ${file.filename}`);\n  }\n}\n\nasync function commitIllustPlan(plan, cookies) {\n  const parent = path.dirname(plan.finalPath);\n  let staging;\n  try {\n    fs.mkdirSync(parent, { recursive: true });\n    staging = fs.mkdtempSync(path.join(parent, `.opencli-${plan.illustId}-`));\n    for (const file of plan.files) {\n      const destination = path.join(staging, file.filename);\n      const result = await httpDownload(file.url, destination, {\n        cookies,\n        headers: { Referer: 'https://www.pixiv.net/' },\n        timeout: 60000,\n        includeContentType: true,\n      });\n      validateImageDownload(result, file);","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/pixiv/bookmark-download.js#L70-L106","documentation":"validateImageDownload verifies that both the HTTP response content type and the final URL's inferred content type match the extension/content type planned for the file. This CommandExecutionError is thrown when Pixiv returned the image but served it with a different MIME type than the download plan expected, so the saved file would likely be the wrong format (e.g. an HTML error page saved as .jpg). The library throws it rather than writing a corrupt or mislabeled file.","triggerScenarios":"commitIllustPlan downloaded an illustration file and called validateImageDownload, but either parsePixivImageUrl(result.finalUrl).contentType or result.contentType (reported by the downloader with includeContentType:true) differs from file.contentType planned for the artwork.","commonSituations":"Pixiv serves a different image format than the filename extension implies (ugoira/webp/jpeg mismatch); a fanbox/i.pximg.net URL redirects to an HTML login or error page whose content type is text/html; the downloader's contentType reporting changed after a library update; a plan file was hand-crafted with the wrong contentType.","solutions":["Delete the partially staged download and retry; a transient server response often resolves on retry","Verify the file.contentType planned for this artwork matches the actual URL's extension (jpg vs png vs gif vs ugoira)","Check that the final URL points at i.pximg.net image content, not an HTML page (inspect result.finalUrl via parsePixivImageUrl)","Confirm the download command/version still supports includeContentType and that result.contentType reflects the served MIME type","Re-run the bookmark download for just that illustId after clearing stale cache/cookies"],"exampleFix":"// before (plan built with assumed type)\n{ filename: '12345.jpg', contentType: 'image/jpeg' }\n// after (derive contentType from the actual source URL)\nconst final = parsePixivImageUrl(sourceUrl, 'Pixiv image download');\n{ filename: `12345${extFor(final.contentType)}`, contentType: final.contentType }","handlingStrategy":"validation","validationCode":"function isSafeDownloadResult(result) {\n  return !!result && typeof result === 'object' && result.success === true &&\n    Number.isSafeInteger(result.size) && result.size > 0 &&\n    typeof result.contentType === 'string' && typeof result.finalUrl === 'string';\n}","typeGuard":"function hasMatchingContentType(result, file) {\n  const final = parsePixivImageUrl(result.finalUrl, 'Pixiv image download');\n  return final.contentType === file.contentType && result.contentType === file.contentType;\n}","tryCatchPattern":"try {\n  validateImageDownload(result, file);\n} catch (err) {\n  if (/unexpected content type/.test(err.message)) {\n    console.warn(`Skipping ${file.filename}: served as ${result?.contentType}, wanted ${file.contentType}`);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Derive file.contentType from the source URL instead of assuming an extension","Inspect result.finalUrl with parsePixivImageUrl before committing","Retry once on MIME mismatches — Pixiv occasionally serves fallback responses","Log finalUrl and both content types on failure for diagnosis"],"tags":["pixiv","download","content-type","validation"],"backgroundTag":"unexpected-content-type","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}