{"record":{"id":"f1d6f29cbfa27e6a","repo":"actualbudget/actual","slug":"useauth-must-be-used-within-an-authprovider","errorCode":null,"errorMessage":"useAuth must be used within an AuthProvider","messagePattern":"useAuth must be used within an AuthProvider","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/auth/AuthProvider.tsx","lineNumber":44,"sourceCode":"    }\n\n    return (\n      !serverUrl ||\n      userData?.permission?.toUpperCase() === permission?.toUpperCase()\n    );\n  };\n\n  return (\n    <AuthContext.Provider value={{ hasPermission }}>\n      {children}\n    </AuthContext.Provider>\n  );\n};\n\nexport const useAuth = () => {\n  const context = useContext(AuthContext);\n  if (context === undefined) {\n    throw new Error('useAuth must be used within an AuthProvider');\n  }\n  return context;\n};\n","sourceCodeStart":26,"sourceCodeEnd":48,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/auth/AuthProvider.tsx#L26-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the consuming component (and its ancestors) inside <AuthProvider>.","In tests, wrap with the provider (or a custom render helper that includes it).","If a portal is involved, render it within the same React tree under AuthProvider.","Check component ordering so the provider mounts before any consumer.","If needed, provide a default context value or move auth logic up to the provider boundary."],"exampleFix":"// before\nrender(<LoginPage />); // throws: outside provider\n// after\nrender(\n  <AuthProvider>\n    <LoginPage />\n  </AuthProvider>\n);","handlingStrategy":"type-guard","validationCode":"const AuthContext = createContext(undefined);\n// Ensure every consumer is inside <AuthProvider>: check the component tree order in App.tsx before using useAuth.","typeGuard":"function hasAuthContext(value) {\n  return value !== undefined && typeof value === 'object' && 'currentUser' in value;\n}\nexport const useAuthSafe = () => {\n  const ctx = useContext(AuthContext);\n  return hasAuthContext(ctx) ? ctx : null;\n};","tryCatchPattern":"// Hooks cannot be wrapped in try/catch; instead use a safe wrapper:\nconst auth = useAuthSafe();\nif (!auth) {\n  return <AuthProvider><App /></AuthProvider>; // or a loading/error state\n}","preventionTips":["Render <AuthProvider> at the very top of the app tree, above all auth consumers.","In tests, use a custom render helper that always wraps with AuthProvider.","Avoid separate React roots for portals that bypass the provider.","Keep auth consumers as descendants of the provider in code reviews."],"tags":["react","context","auth","hooks"],"backgroundTag":"hook-called-outside-provider","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}