{"record":{"id":"c89283981bd1a315","repo":"itwanger/toBeBetterJavaer","slug":"unexpected-content-type-contenttype","errorCode":null,"errorMessage":"Unexpected content type: ${contentType}","messagePattern":"Unexpected content type: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/convert-mdnice-images-to-cdn.js","lineNumber":369,"sourceCode":"      await worker(items[index], index);\n    }\n  });\n  await Promise.all(workers);\n}\n\nasync function downloadImage(url) {\n  const response = await fetch(url, {\n    redirect: \"follow\",\n    headers: {\n      \"User-Agent\": \"toBeBetterJavaer-image-cdn/1.0\",\n    },\n  });\n  if (!response.ok) {\n    throw new Error(`Download failed with HTTP ${response.status}`);\n  }\n  const contentType = normalizeContentType(response.headers.get(\"content-type\"));\n  if (contentType && !contentType.startsWith(\"image/\") && contentType !== \"application/octet-stream\") {\n    throw new Error(`Unexpected content type: ${contentType}`);\n  }\n  const body = Buffer.from(await response.arrayBuffer());\n  if (body.length === 0) {\n    throw new Error(\"Downloaded file is empty\");\n  }\n  return { body, contentType };\n}\n\nfunction normalizeContentType(contentType) {\n  if (!contentType) {\n    return \"\";\n  }\n  return contentType.split(\";\")[0].trim().toLowerCase();\n}\n\nfunction contentTypeForPath(filePath) {\n  const extension = path.extname(filePath).toLowerCase();\n  switch (extension) {","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/itwanger/toBeBetterJavaer/blob/6617f5fd0b0597735bdc028f2fcd036817877327/scripts/convert-mdnice-images-to-cdn.js#L351-L387","documentation":"Thrown by downloadImage() in scripts/convert-mdnice-images-to-cdn.js when a successfully downloaded (HTTP 2xx) response has a Content-Type that, after normalization (strip parameters, lowercase), is neither image/* nor application/octet-stream. This guard prevents uploading HTML error pages, XML, JSON, or text content to the OSS image prefix.","triggerScenarios":"The source URL returns 200 with text/html (a soft-404 error page or a login page), application/json (API error payload), or text/plain. Only application/octet-stream and image/* pass; missing Content-Type passes because contentType is then empty.","commonSituations":"CDN error pages served with status 200; URL redirect chains landing on an HTML interstitial; anti-bot pages; the link pointing at a page rather than the raw image asset.","solutions":["Open the failing URL in a browser or `curl -I` it to see the real Content-Type","If it is an HTML error/login page, the URL is not a direct image link — fix the Markdown reference to the actual asset URL or upload the image manually","If the source genuinely serves the right bytes with a wrong type and you control the script, allow that MIME explicitly in downloadImage"],"exampleFix":"# before: page URL, returns text/html\n![diagram](https://files.mdnice.com/package/cloud/preview.html?id=1)\n\n# after: direct image asset URL\n![diagram](https://files.mdnice.com/user/1234/diagram.png)","handlingStrategy":"validation","validationCode":"const res = await fetch(url, { method: \"HEAD\" });\nconst ct = (res.headers.get(\"content-type\") || \"\").split(\";\")[0].trim().toLowerCase();\nif (ct && !ct.startsWith(\"image/\") && ct !== \"application/octet-stream\") console.warn(`Will be rejected: ${url} is ${ct}`);","typeGuard":"const isAcceptableContentType = (ct) => !ct || ct.startsWith(\"image/\") || ct === \"application/octet-stream\";","tryCatchPattern":"catch (err) { if (err.message.startsWith(\"Unexpected content type:\")) { console.error(\"URL serves a page, not an image — fix the Markdown reference\"); } else throw err; }","preventionTips":["Reference direct asset URLs (ending in .png/.jpg/etc.), never page URLs","Spot-check new mdnice exports with curl -I before batch converting","Keep the accepted-type check in mind when extending --domains to new sources"],"tags":["network","content-type","validation","cdn"],"backgroundTag":null,"analyzedSha":"6617f5fd0b0597735bdc028f2fcd036817877327","analyzedAt":"2026-08-14T14:50:55.107Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}