actualbudget/actual · error

useAuth must be used within an AuthProvider

Error message

useAuth must be used within an AuthProvider

What it means

useAuth is a React context hook that reads the AuthContext created by AuthProvider. If no provider value exists (context === undefined), the hook is being called outside of any <AuthProvider> tree, so there is no auth state to return and the hook throws this descriptive error instead of failing later with an undefined destructuring error.

Solutions

  1. Wrap the consuming component (and its ancestors) inside <AuthProvider>.
  2. In tests, wrap with the provider (or a custom render helper that includes it).
  3. If a portal is involved, render it within the same React tree under AuthProvider.
  4. Check component ordering so the provider mounts before any consumer.
  5. If needed, provide a default context value or move auth logic up to the provider boundary.

Example fix

// before
render(<LoginPage />); // throws: outside provider
// after
render(
  <AuthProvider>
    <LoginPage />
  </AuthProvider>
);
Defensive patterns

Strategy: type-guard

Validate before calling

const AuthContext = createContext(undefined);
// Ensure every consumer is inside <AuthProvider>: check the component tree order in App.tsx before using useAuth.

Type guard

function hasAuthContext(value) {
  return value !== undefined && typeof value === 'object' && 'currentUser' in value;
}
export const useAuthSafe = () => {
  const ctx = useContext(AuthContext);
  return hasAuthContext(ctx) ? ctx : null;
};

Try / catch

// Hooks cannot be wrapped in try/catch; instead use a safe wrapper:
const auth = useAuthSafe();
if (!auth) {
  return <AuthProvider><App /></AuthProvider>; // or a loading/error state
}

Prevention

When it happens

Trigger: Calling useAuth() in a component rendered above or outside <AuthProvider> (e.g. in the app root before the provider mounts), in a component used in tests without wrapping the render in AuthProvider, in a separate React root (modal portal with a different tree root), or in a module evaluated before the provider exists.

Common situations: Refactoring App.tsx and accidentally moving a consumer outside the provider; writing a Vitest/RTL test that renders a component using useAuth without the provider wrapper; rendering a portal under a different React root.

Related errors


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

Appendix: source

Thrown at packages/desktop-client/src/auth/AuthProvider.tsx:44

    }

    return (
      !serverUrl ||
      userData?.permission?.toUpperCase() === permission?.toUpperCase()
    );
  };

  return (
    <AuthContext.Provider value={{ hasPermission }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

View on GitHub (pinned to d4334cb6e6)