itwanger/toBeBetterJavaer · error · Error

Unexpected content type: ${contentType}

Error message

Unexpected content type: ${contentType}

What it means

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.

Source

Thrown at scripts/convert-mdnice-images-to-cdn.js:369

      await worker(items[index], index);
    }
  });
  await Promise.all(workers);
}

async function downloadImage(url) {
  const response = await fetch(url, {
    redirect: "follow",
    headers: {
      "User-Agent": "toBeBetterJavaer-image-cdn/1.0",
    },
  });
  if (!response.ok) {
    throw new Error(`Download failed with HTTP ${response.status}`);
  }
  const contentType = normalizeContentType(response.headers.get("content-type"));
  if (contentType && !contentType.startsWith("image/") && contentType !== "application/octet-stream") {
    throw new Error(`Unexpected content type: ${contentType}`);
  }
  const body = Buffer.from(await response.arrayBuffer());
  if (body.length === 0) {
    throw new Error("Downloaded file is empty");
  }
  return { body, contentType };
}

function normalizeContentType(contentType) {
  if (!contentType) {
    return "";
  }
  return contentType.split(";")[0].trim().toLowerCase();
}

function contentTypeForPath(filePath) {
  const extension = path.extname(filePath).toLowerCase();
  switch (extension) {

View on GitHub (pinned to 6617f5fd0b)

Solutions

  1. Open the failing URL in a browser or `curl -I` it to see the real Content-Type
  2. 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
  3. If the source genuinely serves the right bytes with a wrong type and you control the script, allow that MIME explicitly in downloadImage

Example fix

# before: page URL, returns text/html
![diagram](https://files.mdnice.com/package/cloud/preview.html?id=1)

# after: direct image asset URL
![diagram](https://files.mdnice.com/user/1234/diagram.png)
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(url, { method: "HEAD" });
const ct = (res.headers.get("content-type") || "").split(";")[0].trim().toLowerCase();
if (ct && !ct.startsWith("image/") && ct !== "application/octet-stream") console.warn(`Will be rejected: ${url} is ${ct}`);

Type guard

const isAcceptableContentType = (ct) => !ct || ct.startsWith("image/") || ct === "application/octet-stream";

Try / catch

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; }

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of itwanger/toBeBetterJavaer@6617f5fd0b (2026-08-14). Data as JSON: /api/errors/c89283981bd1a315. Report an issue: GitHub.