adam-p/markdown-here · error · Error

Error fetching local file: ${url}: ${err}

Error message

Error fetching local file: ${url}: ${err}

What it means

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.

Source

Thrown at src/common/utils.js:330

          throw new Error(`Unknown dataType: ${dataType}`);
      }
    })
    .then(data => {
      switch (dataType) {
        case 'text':
        case 'json':
          callback(data);
          break;
        case 'base64':
          data.arrayBuffer().then(function(buffer) {
            var uInt8Array = new Uint8Array(buffer);
            var base64Data = base64EncArr(uInt8Array);
            callback(base64Data);
          });
      }
    })
    .catch(err => {
        throw new Error(`Error fetching local file: ${url}: ${err}`);
    });
}


// Events fired by Markdown Here will have this property set to true.
var MARKDOWN_HERE_EVENT = 'markdown-here-event';

// Fire a mouse event on the given element. (Note: not super robust.)
function fireMouseClick(elem) {
  var clickEvent = elem.ownerDocument.createEvent('MouseEvent');
  clickEvent.initMouseEvent(
    'click',
    true,                           // bubbles: We want the event to bubble.
    true,                           // cancelable
    elem.ownerDocument.defaultView, // view,
    1,                              // detail,
    0,                              // screenX
    0,                              // screenY

View on GitHub (pinned to e00d005299)

Solutions

  1. Validate url and dataType before calling, and confirm the resource exists and is fetchable from the current context (see error[2] fixes).
  2. 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.
  3. Add a global unhandledrejection listener during development to surface these throws, since the callback contract gives no error channel.
  4. 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.

Example fix

// before — errors vanish into an unhandled rejection (callback has no err arg)
getLocalFile(url, 'json', data => render(data));

// after — return the promise so callers can handle failure
function getLocalFile(url, dataType) {
  return fetch(url).then(response => {
    if (!response.ok) throw new Error(`HTTP error status: ${response.status}`);
    if (dataType === 'text')  return response.text();
    if (dataType === 'json')  return response.json();
    if (dataType === 'base64') return response.blob();
    throw new Error(`Unknown dataType: ${dataType}`);
  });
}
// caller
getLocalFile(url, 'json').then(render).catch(err => console.error('load failed', err));
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate inputs before calling; this prevents the 'Unknown dataType' branch
// and reduces needless fetches for obviously bad URLs.
function validateGetLocalFileArgs(url, dataType) {
  if (typeof url !== 'string' || url.length === 0) {
    throw new Error('getLocalFile: url must be a non-empty string');
  }
  if (!['text', 'json', 'base64'].includes(dataType)) {
    throw new Error(`getLocalFile: dataType must be text|json|base64, got ${dataType}`);
  }
}

Type guard

function isValidLocalFileDataType(dataType) {
  return dataType === 'text' || dataType === 'json' || dataType === 'base64';
}

Try / catch

// Because getLocalFile throws inside its internal .catch and returns nothing,
// the rejection is unhandled. Catch it at the process/window boundary and/or
// refactor to return the promise:
if (typeof window !== 'undefined') {
  window.addEventListener('unhandledrejection', evt => {
    if (evt.reason && /Error fetching local file/.test(evt.reason.message)) {
      console.error('local file load failed:', evt.reason);
      evt.preventDefault(); // mark handled
    }
  });
}
// Best fix: change getLocalFile to `return fetch(url)...` so the caller can
// `.catch(err => handleLocalFileError(url, err))` directly.

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of adam-p/markdown-here@e00d005299 (2026-08-13). Data as JSON: /api/errors/ab238c7eb451ba24. Report an issue: GitHub.