{"record":{"id":"ab238c7eb451ba24","repo":"adam-p/markdown-here","slug":"error-fetching-local-file-url-err","errorCode":null,"errorMessage":"Error fetching local file: ${url}: ${err}","messagePattern":"Error fetching local file: (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/common/utils.js","lineNumber":330,"sourceCode":"          throw new Error(`Unknown dataType: ${dataType}`);\n      }\n    })\n    .then(data => {\n      switch (dataType) {\n        case 'text':\n        case 'json':\n          callback(data);\n          break;\n        case 'base64':\n          data.arrayBuffer().then(function(buffer) {\n            var uInt8Array = new Uint8Array(buffer);\n            var base64Data = base64EncArr(uInt8Array);\n            callback(base64Data);\n          });\n      }\n    })\n    .catch(err => {\n        throw new Error(`Error fetching local file: ${url}: ${err}`);\n    });\n}\n\n\n// Events fired by Markdown Here will have this property set to true.\nvar MARKDOWN_HERE_EVENT = 'markdown-here-event';\n\n// Fire a mouse event on the given element. (Note: not super robust.)\nfunction fireMouseClick(elem) {\n  var clickEvent = elem.ownerDocument.createEvent('MouseEvent');\n  clickEvent.initMouseEvent(\n    'click',\n    true,                           // bubbles: We want the event to bubble.\n    true,                           // cancelable\n    elem.ownerDocument.defaultView, // view,\n    1,                              // detail,\n    0,                              // screenX\n    0,                              // screenY","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/adam-p/markdown-here/blob/e00d005299922198aef968e0cd42b275525c20a6/src/common/utils.js#L312-L348","documentation":"The .catch at the end of getLocalFile's fetch chain re-throws every failure as `Error fetching local file: <url>: <err>`, wrapping the underlying cause (a network failure, the HTTP-status error from error [2], an unknown dataType, or a response.json() parse failure). Crucially, getLocalFile does not return the promise and its callback signature has no error parameter, so this throw is not catchable by the caller directly — it becomes an unhandled promise rejection. Note also that error[2] is always re-wrapped here, so callers effectively see this message rather than the raw HTTP-status one.","triggerScenarios":"fetch() rejects (network error, blocked by CSP, missing host/extension permissions, CORS in a non-extension context); a non-2xx response triggers error[2] which is then caught and re-wrapped here; response.json() throws on malformed JSON; response.blob()/arrayBuffer() fails; an unsupported dataType is passed.","commonSituations":"Extension missing host_permissions for the URL; CSP blocking the request; corrupt or empty 'json' file; calling getLocalFile outside the extension where fetch cannot resolve a chrome-extension:// URL; passing a dataType other than text/json/base64.","solutions":["Validate url and dataType before calling, and confirm the resource exists and is fetchable from the current context (see error[2] fixes).","Treat getLocalFile as fallible: refactor it to return the promise (per the TODO) so the caller can .catch the rejection instead of relying on the global unhandledrejection handler.","Add a global unhandledrejection listener during development to surface these throws, since the callback contract gives no error channel.","For json files, pre-validate that the file is non-empty and parses, or switch the dataType to 'text' and JSON.parse in the callback where you can try/catch."],"exampleFix":"// before — errors vanish into an unhandled rejection (callback has no err arg)\ngetLocalFile(url, 'json', data => render(data));\n\n// after — return the promise so callers can handle failure\nfunction getLocalFile(url, dataType) {\n  return fetch(url).then(response => {\n    if (!response.ok) throw new Error(`HTTP error status: ${response.status}`);\n    if (dataType === 'text')  return response.text();\n    if (dataType === 'json')  return response.json();\n    if (dataType === 'base64') return response.blob();\n    throw new Error(`Unknown dataType: ${dataType}`);\n  });\n}\n// caller\ngetLocalFile(url, 'json').then(render).catch(err => console.error('load failed', err));","handlingStrategy":"try-catch","validationCode":"// Validate inputs before calling; this prevents the 'Unknown dataType' branch\n// and reduces needless fetches for obviously bad URLs.\nfunction validateGetLocalFileArgs(url, dataType) {\n  if (typeof url !== 'string' || url.length === 0) {\n    throw new Error('getLocalFile: url must be a non-empty string');\n  }\n  if (!['text', 'json', 'base64'].includes(dataType)) {\n    throw new Error(`getLocalFile: dataType must be text|json|base64, got ${dataType}`);\n  }\n}","typeGuard":"function isValidLocalFileDataType(dataType) {\n  return dataType === 'text' || dataType === 'json' || dataType === 'base64';\n}","tryCatchPattern":"// Because getLocalFile throws inside its internal .catch and returns nothing,\n// the rejection is unhandled. Catch it at the process/window boundary and/or\n// refactor to return the promise:\nif (typeof window !== 'undefined') {\n  window.addEventListener('unhandledrejection', evt => {\n    if (evt.reason && /Error fetching local file/.test(evt.reason.message)) {\n      console.error('local file load failed:', evt.reason);\n      evt.preventDefault(); // mark handled\n    }\n  });\n}\n// Best fix: change getLocalFile to `return fetch(url)...` so the caller can\n// `.catch(err => handleLocalFileError(url, err))` directly.","preventionTips":["Do not assume getLocalFile succeeds — its callback has no error channel and rejections currently escape as unhandled.","Add a global unhandledrejection listener during development to surface these wrapped errors.","Pre-validate dataType against text|json|base64 to avoid the Unknown dataType throw.","Refactor getLocalFile to return its promise (the code's own TODO) so failures are catchable at the call site."],"tags":["network","promises","unhandled-rejection","fetch","extension","error-handling"],"backgroundTag":null,"analyzedSha":"e00d005299922198aef968e0cd42b275525c20a6","analyzedAt":"2026-08-13T00:39:36.904Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}