{"record":{"id":"9d60aed715d0fa73","repo":"gildas-lormeau/SingleFile","slug":"index-page-not-found-error","errorCode":"INDEX_PAGE_NOT_FOUND_ERROR","errorMessage":"Index page not found","messagePattern":"Index page not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/mhtml-to-html/convert.js","lineNumber":241,"sourceCode":"    }\n}\n\nfunction getBackoffDelay(indexAttempt) {\n    return Math.min(RETRY_BASE_DELAY * Math.pow(2, indexAttempt), MAX_RETRY_DELAY);\n}\n\nfunction wait(delay) {\n    return new Promise(resolve => setTimeout(resolve, delay));\n}\n\nfunction convert({ headers, frames, resources, unfoundResources = new Set(), index, id, anomalies = [] }, { DOMParser, enableScripts, fetchMissingResources } = { DOMParser: globalThis.DOMParser }) {\n    let resource = resources[index];\n    if (!resource) {\n        // an archive does not have to hold a page: a browser saves a standalone image or text file\n        // the same way, and presents it as a document built around that single resource\n        resource = createDocument(resources);\n        if (!resource) {\n            throw new Error(INDEX_PAGE_NOT_FOUND_ERROR);\n        }\n        index = resource.id;\n        if (!fetchMissingResources) {\n            // reported on a copy rather than pushed into the parse result, so converting the same\n            // archive again reports it once again instead of twice\n            anomalies = [...anomalies, { type: SYNTHESIZED_INDEX_ANOMALY, id: index }];\n        }\n    }\n    let base = resource.id;\n    if (resource.transferEncoding === BASE64_ENCODING) {\n        resource.transferEncoding = undefined;\n        resource.data = decodeBase64(resource.data, getCharset(resource.contentType));\n    }\n    // a part is not required to declare a type, and parseDOM falls back to HTML when it does not\n    const contentType = resource.contentType ? resource.contentType.split(CONTENT_TYPE_SEPARATOR)[0] : undefined;\n    const dom = getResourceDOM(resource, contentType, DOMParser);\n    if (!fetchMissingResources) {\n        // the rewrite mutates the tree it walks, so the cached one is given up: were the same","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/lib/mhtml-to-html/convert.js#L223-L259","documentation":"convert() walks the MHTML archive's resources looking for the index page; if none of the resources qualifies as an index and it cannot synthesize a document (createDocument returns null), it throws INDEX_PAGE_NOT_FOUND_ERROR. This happens because an archive is not required to contain an HTML page — browsers also save standalone images or text files in MHTML format.","triggerScenarios":"Passing an MHTML file whose resources contain no HTML document and whose single resource cannot be turned into a document (e.g., a binary/image-only archive where createDocument fails, or an empty/corrupt resource list).","commonSituations":"Converting an MHTML file saved from a direct image download link; a truncated or corrupt .mhtml file with an empty or malformed resource table; feeding a non-MHTML file (plain text, partial download) to the converter.","solutions":["Verify the input file is a valid MHTML archive containing an HTML document (open it in a text editor and look for text/html parts)","Re-save the page from the browser as MHTML (single-file or complete-page option)","Handle the INDEX_PAGE_NOT_FOUND_ERROR code in a catch block and fall back to treating the file as a plain attachment rather than a document","If processing user uploads, validate the MIME type/content before invoking convert()"],"exampleFix":"// before\nconst result = convert(mhtmlBuffer);\n// after\nlet result;\ntry {\n  result = convert(mhtmlBuffer);\n} catch (e) {\n  if (e.code === \"INDEX_PAGE_NOT_FOUND_ERROR\") result = null; // not a page archive\n  else throw e;\n}","handlingStrategy":"validation","validationCode":"function looksLikeMhtml(buf) {\n  const head = buf.slice(0, 512).toString(\"latin1\");\n  return head.includes(\"MIME-Version\") && head.includes(\"multipart/related\");\n}\nif (!looksLikeMhtml(mhtmlBuffer)) throw new Error(\"Input is not a valid MHTML archive\");","typeGuard":"function hasIndexResource(resources) {\n  return Array.isArray(resources) && resources.some(r => r && typeof r.id !== \"undefined\");\n}","tryCatchPattern":"try {\n  const result = convert(buffer);\n} catch (e) {\n  if (e.code === \"INDEX_PAGE_NOT_FOUND_ERROR\") {\n    // archive holds no HTML page (e.g. saved image); handle as attachment\n    return null;\n  }\n  throw e;\n}","preventionTips":["Validate uploads are real MHTML (multipart/related) before converting","Re-save corrupt/truncated archives from the browser","Treat single-resource archives (images/text) as a separate code path","Unit-test conversion with page, image-only, and empty archives"],"tags":["mhtml","parsing","conversion"],"backgroundTag":"missing-index-document","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}