actualbudget/actual · error · LazyLoadFailedError

Error: failed loading lazy-loaded module ${name}

Error message

Error: failed loading lazy-loaded module ${name}

What it means

LoadComponent lazy-imports modal components by name; when the dynamic `importer()` promise rejects (even after 5 exponential-backoff retries via promiseRetry), it throws LazyLoadFailedError wrapping the underlying error and the module name. This means the code-split chunk for the modal could not be fetched or evaluated, so the UI cannot render it.

Source

Thrown at packages/desktop-client/src/components/util/LoadComponent.tsx:63

            }
          })
          .catch(retry),
      {
        retries: 5,
      },
    ).catch(e => {
      if (!isUnmounted) {
        setError(e);
      }
    });

    return () => {
      isUnmounted = true;
    };
  }, [name, importer]);

  if (error) {
    throw new LazyLoadFailedError(name, error);
  }

  if (!Component) {
    return (
      <View
        style={{
          flex: 1,
          gap: 20,
          justifyContent: 'center',
          alignItems: 'center',
          ...styles.delayedFadeIn,
        }}
      >
        {message && (
          <Block style={{ marginBottom: 20, fontSize: 18 }}>{message}</Block>
        )}
        <AnimatedLoading width={25} color={theme.pageTextDark} />
      </View>

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Reload the page (or force a hard refresh) to fetch the current build's chunk manifest.
  2. Check the browser Network tab for the failing chunk request — 404 indicates a stale build; fix by redeploying or reloading.
  3. Verify the importer path and the exported component name still match after refactors (`module[name]` must exist).
  4. Add an error boundary around LoadComponent that offers a 'reload' action, and/or auto-reload once when LazyLoadFailedError is chunk-load related.

Example fix

// before
if (error) {
  throw new LazyLoadFailedError(name, error);
}
// after
if (error) {
  if (isFirstLoadFailure) {
    window.location.reload(); // recover from stale-chunk deployments
    return null;
  }
  throw new LazyLoadFailedError(name, error);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before relying on a lazy modal, verify navigator.onLine and that the app build is current
if (!navigator.onLine) {
  showOfflineNotice();
  return;
}
// optionally probe the chunk URL
const res = await fetch(chunkUrl, { method: 'HEAD' });
if (!res.ok) window.location.reload();

Type guard

import { LazyLoadFailedError } from '@actual-app/core/shared/errors';
function isLazyLoadFailed(e: unknown): e is LazyLoadFailedError {
  return e instanceof LazyLoadFailedError;
}

Try / catch

<ErrorBoundary
  fallbackRender={({ error, resetErrorBoundary }) =>
    isLazyLoadFailed(error) ? (
      <RetryLoadScreen onReload={() => { window.location.reload(); }} />
    ) : (
      <UnexpectedErrorScreen error={error} onDismiss={resetErrorBoundary} />
    )
  }
>
  <LoadComponent name="ManageRulesModal" importer={...} />
</ErrorBoundary>

Prevention

When it happens

Trigger: A failed dynamic import: network outage or interrupted connection while fetching the chunk, deploying a new app version so old HTML references chunk filenames that no longer exist, a service worker caching stale chunks, or the importer resolving but `module[name]` being undefined after a rename.

Common situations: Users with a long-lived open tab crossing a deployment (stale index.html requesting deleted hashed chunks); flaky/offline connections on mobile; CDN misconfiguration returning HTML instead of JS for chunk URLs.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/f494be93740b5def. Report an issue: GitHub.