{"record":{"id":"9fc52191ad017497","repo":"actualbudget/actual","slug":"servercontext-not-initialized","errorCode":null,"errorMessage":"ServerContext not initialized","messagePattern":"ServerContext not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/ServerContext.tsx","lineNumber":48,"sourceCode":"  setURL: (\n    url: string,\n    opts?: { validate?: boolean },\n  ) => Promise<{ error?: string }>;\n  refreshLoginMethods: () => Promise<void>;\n  setMultiuserEnabled: (enabled: boolean) => void;\n  setLoginMethods: (methods: LoginMethod[]) => void;\n};\n\nconst ServerContext = createContext<ServerContextValue>({\n  url: null,\n  version: '',\n  multiuserEnabled: false,\n  availableLoginMethods: [],\n  setURL: () => Promise.reject(new Error('ServerContext not initialized')),\n  refreshLoginMethods: () =>\n    Promise.reject(new Error('ServerContext not initialized')),\n  setMultiuserEnabled: () => {\n    throw new Error('ServerContext not initialized');\n  },\n  setLoginMethods: () => {\n    throw new Error('ServerContext not initialized');\n  },\n});\n\nexport const useServerURL = () => useContext(ServerContext).url;\nexport const useServerVersion = () => useContext(ServerContext).version;\nexport const useSetServerURL = () => useContext(ServerContext).setURL;\nexport const useMultiuserEnabled = () => {\n  const { multiuserEnabled } = useContext(ServerContext);\n  const loginMethod = useLoginMethod();\n  return multiuserEnabled && loginMethod === 'openid';\n};\n\nexport const useLoginMethod = () => {\n  const availableLoginMethods = useContext(ServerContext).availableLoginMethods;\n","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/ServerContext.tsx#L30-L66","documentation":"ServerContext is a React context exposing server connection info (url, version, multiuserEnabled, loginMethods). Its createContext default object contains deliberately-throwing stubs so that calls made before the provider mounts fail loudly instead of silently doing nothing. `setURL` rejects with this error because the default provider has never replaced the stub.","triggerScenarios":"Calling `useSetServerURL()(...)` (or consuming ServerContext.setURL directly) in a component rendered OUTSIDE the <ServerProvider> tree, or during a window where the provider hasn't mounted (e.g. in tests rendering the component in isolation).","commonSituations":"Unit tests rendering a component with React Testing Library without wrapping it in ServerProvider; storybook stories rendering a settings page standalone; a code change moving a component outside the provider in the component tree; calling setURL before the app bootstraps the provider.","solutions":["Wrap the component (or test render) in the ServerProvider from packages/desktop-client/src/components/ServerContext.tsx so the real implementation replaces the default stubs.","In tests, render with the app's standard test wrapper/helper that already includes ServerProvider (or mock the context value with a vi.fn resolving instead of rejecting).","Verify the hook is consumed below the provider — move the component into the provider subtree rather than calling context methods at module scope."],"exampleFix":"// before (test)\nrender(<ServerSettings />);\n\n// after\nrender(\n  <ServerProvider>\n    <ServerSettings />\n  </ServerProvider>\n);","handlingStrategy":"try-catch","validationCode":"const ctx = useContext(ServerContext);\nconst isInitialized = ctx.url !== undefined && typeof ctx.setURL === 'function' && !String(ctx.setURL).includes('not initialized');\nif (!isInitialized) throw new Error('ServerProvider missing — wrap component in <ServerProvider>');","typeGuard":"function hasServerContext(ctx): ctx is ServerContextValue & { setURL: (url: string) => Promise<void> } {\n  return typeof ctx?.setURL === 'function' && ctx.url != null;\n}","tryCatchPattern":"try {\n  await setURL(newUrl);\n} catch (e) {\n  if (e.message.includes('ServerContext not initialized')) {\n    // provider missing: skip persisting or fall back to local state\n  } else throw e;\n}","preventionTips":["Always render server-dependent components inside ServerProvider, including test/story renders.","Use the app's shared test wrapper so the provider is never forgotten.","Never destructure context methods at module scope; call them inside components under the provider."],"tags":["react","context","provider-missing","desktop-client"],"backgroundTag":"context-used-outside-provider","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}