index.js 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. const objectToString = Object.prototype.toString;
  2. const isError = value => objectToString.call(value) === '[object Error]';
  3. const errorMessages = new Set([
  4. 'network error', // Chrome
  5. 'NetworkError when attempting to fetch resource.', // Firefox
  6. 'The Internet connection appears to be offline.', // Safari 16
  7. 'Network request failed', // `cross-fetch`
  8. 'fetch failed', // Undici (Node.js)
  9. 'terminated', // Undici (Node.js)
  10. ' A network error occurred.', // Bun (WebKit)
  11. 'Network connection lost', // Cloudflare Workers (fetch)
  12. ]);
  13. export default function isNetworkError(error) {
  14. const isValid = error
  15. && isError(error)
  16. && error.name === 'TypeError'
  17. && typeof error.message === 'string';
  18. if (!isValid) {
  19. return false;
  20. }
  21. const {message, stack} = error;
  22. // Safari 17+ has generic message but no stack for network errors
  23. if (message === 'Load failed') {
  24. return stack === undefined
  25. // Sentry adds its own stack trace to the fetch error, so also check for that
  26. || '__sentry_captured__' in error;
  27. }
  28. // Deno network errors start with specific text
  29. if (message.startsWith('error sending request for url')) {
  30. return true;
  31. }
  32. // Chrome: exact "Failed to fetch" or with hostname: "Failed to fetch (example.com)"
  33. if (message === 'Failed to fetch' || (message.startsWith('Failed to fetch (') && message.endsWith(')'))) {
  34. return true;
  35. }
  36. // Standard network error messages
  37. return errorMessages.has(message);
  38. }