jackwener/OpenCLI · error · CommandExecutionError

Pixiv image download returned unexpected content type for ${

Error message

Pixiv image download returned unexpected content type for ${file.filename}

What it means

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.

Source

Thrown at clis/pixiv/bookmark-download.js:88

  const finalPath = path.join(outputRoot, 'illust', row.illust_id);
  if (pixivPathEntryExists(finalPath)) {
    throw new CommandExecutionError(`Refusing to overwrite existing Pixiv download: ${finalPath}`);
  }
  const createdDirs = [];
  for (let cursor = path.dirname(finalPath); !fs.existsSync(cursor); cursor = path.dirname(cursor)) {
    createdDirs.push(cursor);
    if (path.dirname(cursor) === cursor) break;
  }
  return { kind: 'illust', illustId: row.illust_id, finalPath, files, createdDirs };
}

function validateImageDownload(result, file) {
  if (!result || typeof result !== 'object' || result.success !== true || !Number.isSafeInteger(result.size) || result.size <= 0) {
    throw new CommandExecutionError(`Pixiv image download failed: ${result?.error || 'invalid download result'}`);
  }
  const final = parsePixivImageUrl(result.finalUrl, 'Pixiv image download');
  if (final.contentType !== file.contentType || result.contentType !== file.contentType) {
    throw new CommandExecutionError(`Pixiv image download returned unexpected content type for ${file.filename}`);
  }
}

async function commitIllustPlan(plan, cookies) {
  const parent = path.dirname(plan.finalPath);
  let staging;
  try {
    fs.mkdirSync(parent, { recursive: true });
    staging = fs.mkdtempSync(path.join(parent, `.opencli-${plan.illustId}-`));
    for (const file of plan.files) {
      const destination = path.join(staging, file.filename);
      const result = await httpDownload(file.url, destination, {
        cookies,
        headers: { Referer: 'https://www.pixiv.net/' },
        timeout: 60000,
        includeContentType: true,
      });
      validateImageDownload(result, file);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Delete the partially staged download and retry; a transient server response often resolves on retry
  2. Verify the file.contentType planned for this artwork matches the actual URL's extension (jpg vs png vs gif vs ugoira)
  3. Check that the final URL points at i.pximg.net image content, not an HTML page (inspect result.finalUrl via parsePixivImageUrl)
  4. Confirm the download command/version still supports includeContentType and that result.contentType reflects the served MIME type
  5. Re-run the bookmark download for just that illustId after clearing stale cache/cookies

Example fix

// before (plan built with assumed type)
{ filename: '12345.jpg', contentType: 'image/jpeg' }
// after (derive contentType from the actual source URL)
const final = parsePixivImageUrl(sourceUrl, 'Pixiv image download');
{ filename: `12345${extFor(final.contentType)}`, contentType: final.contentType }
Defensive patterns

Strategy: validation

Validate before calling

function isSafeDownloadResult(result) {
  return !!result && typeof result === 'object' && result.success === true &&
    Number.isSafeInteger(result.size) && result.size > 0 &&
    typeof result.contentType === 'string' && typeof result.finalUrl === 'string';
}

Type guard

function hasMatchingContentType(result, file) {
  const final = parsePixivImageUrl(result.finalUrl, 'Pixiv image download');
  return final.contentType === file.contentType && result.contentType === file.contentType;
}

Try / catch

try {
  validateImageDownload(result, file);
} catch (err) {
  if (/unexpected content type/.test(err.message)) {
    console.warn(`Skipping ${file.filename}: served as ${result?.contentType}, wanted ${file.contentType}`);
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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