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
- 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
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
- 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
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
- Midjourney media download returned unexpected content type "
- Pixiv image download did not create a valid file: ${file.fil
- Refusing to overwrite existing Pixiv download: ${plan.finalP
- Pixiv illustration ${plan.illustId} download failed: ${error
- Pixiv cookie lookup returned malformed entries: ${error?.mes
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4c713119cc9815ce.
Report an issue: GitHub.