{"record":{"id":"7ba6f5bffa329b9c","repo":"adam-p/markdown-here","slug":"http-error-status-response-status","errorCode":null,"errorMessage":"HTTP error status: ${response.status}","messagePattern":"HTTP error status: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/common/utils.js","lineNumber":301,"sourceCode":"    return url;\n  }\n\n  return chrome.runtime.getURL(url);\n}\n\n\n// Makes an asynchronous XHR request for a local file (basically a thin wrapper).\n// `dataType` must be one of 'text', 'json', or 'base64'.\n// `callback` will be called with the response value, of a type depending on `dataType`.\n// Errors are not expected for local files, and will result in an exception being thrown asynchronously.\n// TODO: Return a promise instead of using a callback. This will allow returning an error\n// properly, and then this can be used in options.js when checking for the existence of\n// the test file.\nfunction getLocalFile(url, dataType, callback) {\n  fetch(url)\n    .then(response => {\n      if (!response.ok) {\n        throw new Error(`HTTP error status: ${response.status}`);\n      }\n\n      switch (dataType) {\n        case 'text':\n          return response.text();\n        case 'json':\n          return response.json();\n        case 'base64':\n          return response.blob();\n        default:\n          throw new Error(`Unknown dataType: ${dataType}`);\n      }\n    })\n    .then(data => {\n      switch (dataType) {\n        case 'text':\n        case 'json':\n          callback(data);","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/adam-p/markdown-here/blob/e00d005299922198aef968e0cd42b275525c20a6/src/common/utils.js#L283-L319","documentation":"getLocalFile(url, dataType, callback) wraps fetch() to read extension-bundled local resources and rejects any non-2xx response by throwing `HTTP error status: <status>` when response.ok is false. The function is documented as expecting local files that 'are not expected to fail'; a non-OK status is treated as an exceptional condition rather than a recoverable return value. Because the throw happens inside the fetch .then chain and getLocalFile does not return the promise, it surfaces downstream as the wrapped rejection in error [3].","triggerScenarios":"fetch() resolves with a 404/403/500 (etc.) for the given url — most commonly a file path that does not exist, a file not listed in the extension manifest's web_accessible_resources, or a URL resolved against the wrong base. Also a relative url that was not converted with chrome.runtime.getURL before being passed in.","commonSituations":"Asset was renamed or moved but the caller still requests the old path; new resource added but not declared in manifest.json web_accessible_resources (causing 404/forbidden in extension context); path typos; deploying to a context where chrome.runtime.getURL was not applied so the URL resolves against the document origin instead of the extension.","solutions":["Verify the url exists at the exact path passed and log chrome.runtime.getURL(url) to confirm the resolved extension URL.","Add the resource to manifest.json web_accessible_resources (and match the right extension/extension_ids) if it is fetched from a web context.","Correct a relative path by passing it through the project's getURL()/chrome.runtime.getURL() helper first.","If the file may legitimately be absent (e.g. the test-file existence check noted in the TODO), refactor to return the promise so the caller can branch on status instead of throwing."],"exampleFix":"// before\ngetLocalFile('defaults.json', 'json', cb); // 404 → throws HTTP error status: 404\n\n// after — ensure correct extension-relative URL and manifest entry\ngetLocalFile(chrome.runtime.getURL('defaults.json'), 'json', cb);\n// manifest.json:\n// \"web_accessible_resources\": [{ \"resources\": [\"defaults.json\"], \"matches\": [\"<all_urls>\"] }]","handlingStrategy":"try-catch","validationCode":"// Best-effort pre-flight: resolve the extension URL and confirm it is in\n// web_accessible_resources before calling getLocalFile.\nfunction resolveLocalUrl(rawUrl) {\n  const fullUrl = (typeof chrome !== 'undefined' && chrome.runtime)\n    ? chrome.runtime.getURL(rawUrl)\n    : rawUrl;\n  return fullUrl;\n}\n// Note: a 200 cannot be fully guaranteed without fetching; pair with try-catch.","typeGuard":null,"tryCatchPattern":"// getLocalFile surfaces failures as an unhandled rejection, not via callback,\n// so wrap it in a promise you control.\nfunction getLocalFileP(url, dataType) {\n  return new Promise((resolve, reject) => {\n    getLocalFile(url, dataType, resolve);\n    // rejection path: attach a window-level unhandledrejection listener, or\n    // refactor getLocalFile to return its promise (preferred).\n  });\n}\n// Preferred: refactor getLocalFile to `return fetch(url).then(...)` and let the\n// caller do `.catch(err => { if (/HTTP error status: (\\d+)/.test(err.message)) ... })`.","preventionTips":["Always pass extension resources through chrome.runtime.getURL before fetching.","Declare every fetched asset in manifest.json web_accessible_resources.","Log the resolved URL at the call site during development to catch path/typos.","Treat a missing local file as a real error path — the function's own comment admits the TODO to return a promise so callers can branch."],"tags":["network","fetch","http","extension","manifest","file-io"],"backgroundTag":null,"analyzedSha":"e00d005299922198aef968e0cd42b275525c20a6","analyzedAt":"2026-08-13T00:39:36.904Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}