actualbudget/actual · error

ServerContext not initialized

Error message

ServerContext not initialized

What it means

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.

Source

Thrown at packages/desktop-client/src/components/ServerContext.tsx:48

  setURL: (
    url: string,
    opts?: { validate?: boolean },
  ) => Promise<{ error?: string }>;
  refreshLoginMethods: () => Promise<void>;
  setMultiuserEnabled: (enabled: boolean) => void;
  setLoginMethods: (methods: LoginMethod[]) => void;
};

const ServerContext = createContext<ServerContextValue>({
  url: null,
  version: '',
  multiuserEnabled: false,
  availableLoginMethods: [],
  setURL: () => Promise.reject(new Error('ServerContext not initialized')),
  refreshLoginMethods: () =>
    Promise.reject(new Error('ServerContext not initialized')),
  setMultiuserEnabled: () => {
    throw new Error('ServerContext not initialized');
  },
  setLoginMethods: () => {
    throw new Error('ServerContext not initialized');
  },
});

export const useServerURL = () => useContext(ServerContext).url;
export const useServerVersion = () => useContext(ServerContext).version;
export const useSetServerURL = () => useContext(ServerContext).setURL;
export const useMultiuserEnabled = () => {
  const { multiuserEnabled } = useContext(ServerContext);
  const loginMethod = useLoginMethod();
  return multiuserEnabled && loginMethod === 'openid';
};

export const useLoginMethod = () => {
  const availableLoginMethods = useContext(ServerContext).availableLoginMethods;

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. 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.
  2. 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).
  3. Verify the hook is consumed below the provider — move the component into the provider subtree rather than calling context methods at module scope.

Example fix

// before (test)
render(<ServerSettings />);

// after
render(
  <ServerProvider>
    <ServerSettings />
  </ServerProvider>
);
Defensive patterns

Strategy: try-catch

Validate before calling

const ctx = useContext(ServerContext);
const isInitialized = ctx.url !== undefined && typeof ctx.setURL === 'function' && !String(ctx.setURL).includes('not initialized');
if (!isInitialized) throw new Error('ServerProvider missing — wrap component in <ServerProvider>');

Type guard

function hasServerContext(ctx): ctx is ServerContextValue & { setURL: (url: string) => Promise<void> } {
  return typeof ctx?.setURL === 'function' && ctx.url != null;
}

Try / catch

try {
  await setURL(newUrl);
} catch (e) {
  if (e.message.includes('ServerContext not initialized')) {
    // provider missing: skip persisting or fall back to local state
  } else throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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