{"record":{"id":"daef83001a4ff0d2","repo":"cheeriojs/cheerio","slug":"the-content-type-mimetype-essence-is-neither","errorCode":null,"errorMessage":"The content-type \"${mimeType.essence}\" is neither HTML nor XML.","messagePattern":"The content-type \"(.+?)\" is neither HTML nor XML\\.","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"src/index.ts","lineNumber":263,"sourceCode":"  const promise = new Promise<CheerioAPI>((resolve, reject) => {\n    undiciStream = new Client(urlObject.origin)\n      .compose(interceptors.redirect({ maxRedirections: 5 }))\n      .stream(streamOptions, (res) => {\n        if (res.statusCode < 200 || res.statusCode >= 300) {\n          throw new errors.ResponseError('Response Error', res.statusCode, {\n            headers: res.headers,\n          });\n        }\n\n        const contentTypeHeader = res.headers['content-type'] ?? 'text/html';\n        const mimeType = new MIMEType(\n          Array.isArray(contentTypeHeader)\n            ? contentTypeHeader[0]\n            : contentTypeHeader,\n        );\n\n        if (!(mimeType.isHTML() || mimeType.isXML())) {\n          throw new RangeError(\n            `The content-type \"${mimeType.essence}\" is neither HTML nor XML.`,\n          );\n        }\n\n        // Forward the charset from the header to the decodeStream.\n        encoding.transportLayerEncodingLabel =\n          mimeType.parameters.get('charset');\n\n        /*\n         * If we allow redirects, we will have entries in the history.\n         * The last entry will be the final URL.\n         */\n        const history = (\n          res.context as\n            | {\n                history?: URL[];\n              }\n            | undefined","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/cheeriojs/cheerio/blob/a1be131f9b0cef323ef0d65c5babda561413ac7d/src/index.ts#L245-L281","documentation":"fromURL() fetches a URL and only parses HTML or XML responses. It parses the Content-Type header; if the MIME type is neither HTML nor XML (e.g. application/json, text/plain, image/*), it throws a RangeError because the parser cannot meaningfully process the body.","triggerScenarios":"Calling cheerio.fromURL() on an endpoint that returns JSON (an API URL instead of a page), a text file, a redirect landing on a download, or a misconfigured server sending the wrong Content-Type.","commonSituations":"Pointing fromURL at a REST API instead of the HTML page; servers responding with application/octet-stream or text/plain for HTML; SPA backends returning JSON for unknown routes; API gateways rewriting content types.","solutions":["Verify the URL actually returns HTML (curl -I <url>) and fix the URL/endpoint","If the server mislabels HTML, fetch manually and pass the body to cheerio.load(body) with xml/HTML parsing forced","Handle JSON endpoints with a JSON parser instead of cheerio","Catch the RangeError and fall back to manual fetch + load"],"exampleFix":"// before\nconst $ = await cheerio.fromURL('https://api.example.com/v1/page');\n// after\nconst res = await fetch('https://api.example.com/v1/page');\nconst $ = cheerio.load(await res.text()); // parse regardless of content-type","handlingStrategy":"fallback","validationCode":"const res = await fetch(url);\nconst type = res.headers.get('content-type') ?? '';\nif (!/html|xml/i.test(type)) {\n  throw new Error(`Refusing to parse ${type} from ${url}`);\n}\nconst $ = await cheerio.fromURL(url);","typeGuard":"const isHtmlOrXml = (contentType: string | null): boolean =>\n  /text\\/html|application\\/x?html|\\+xml|application\\/xml/i.test(contentType ?? '');","tryCatchPattern":"try {\n  const $ = await cheerio.fromURL(url);\n} catch (e) {\n  if (e instanceof RangeError && /neither HTML nor XML/.test(e.message)) {\n    const res = await fetch(url);\n    return cheerio.load(await res.text()); // manual fallback\n  }\n  throw e;\n}","preventionTips":["Check Content-Type with curl -I before scraping new URLs","Expect APIs to return JSON — parse those with JSON.parse, not cheerio","Wrap scrapers so a bad content-type degrades gracefully instead of crashing"],"tags":["cheerio","fromurl","content-type","http","fetch","rangeerror"],"backgroundTag":"unexpected-content-type","analyzedSha":"a1be131f9b0cef323ef0d65c5babda561413ac7d","analyzedAt":"2026-08-28T13:05:26.741Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}