actualbudget/actual · error

Invalid OpenID configuration

Error message

Invalid OpenID configuration

What it means

The sync-server's OpenID status endpoint failed to JSON.parse the stored `extra_data` blob for the OpenID configuration and returns HTTP 500 with reason 'Invalid OpenID configuration'. The server wraps OpenID discovery/issuer metadata in the user's auth extra_data column; if that column does not contain valid JSON (corrupt write, manual DB edit, or legacy string format), parsing throws and the catch branch responds with this error. It signals stored configuration corruption, not a live issuer problem.

Source

Thrown at packages/sync-server/src/app-openid.ts:94

    res.status(400).send({ status: 'error', reason: 'invalid-password' });
    return;
  }

  const auth = UserService.getOpenIDConfig();

  if (!auth) {
    res
      .status(500)
      .send({ status: 'error', reason: 'OpenID configuration not found' });
    return;
  }

  try {
    const openIdConfig = JSON.parse(auth.extra_data);
    res.send({ status: 'ok', data: { openId: openIdConfig } });
  } catch {
    res
      .status(500)
      .send({ status: 'error', reason: 'Invalid OpenID configuration' });
  }
});

app.get('/callback', async (req, res) => {
  const { error, url } = await loginWithOpenIdFinalize(req.query);

  if (error) {
    res.status(400).send({ status: 'error', reason: error });
    return;
  }

  if (!isValidRedirectUrl(url)) {
    res.status(400).send({ status: 'error', reason: 'Invalid redirect URL' });
    return;
  }

  res.redirect(url);

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Re-save the OpenID configuration in the server settings so extra_data is rewritten as valid JSON
  2. Inspect the auth_methods table row and fix or delete the corrupt extra_data value
  3. Delete the affected auth method and re-add the OpenID provider
  4. Check server release notes for migrations that convert extra_data format and re-run them

Example fix

// before (corrupt row)
extra_data = 'issuer=https://id.example.com'
// after
extra_data = '{"issuer":"https://id.example.com","client_id":"...","client_secret":"..."}'
Defensive patterns

Strategy: validation

Validate before calling

function isOpenIdConfigData(extraData) {
  try {
    const cfg = JSON.parse(extraData);
    return cfg && typeof cfg === 'object' && typeof cfg.issuer === 'string';
  } catch { return false; }
}
// call before relying on the /openid/status response

Type guard

function isOpenIdConfig(v) {
  return v !== null && typeof v === 'object' && 'issuer' in v;
}

Try / catch

try {
  const res = await fetch('/openid/status');
  const body = await res.json();
  if (body.status === 'error' && body.reason === 'Invalid OpenID configuration') {
    await reSaveOpenIdConfig(); // rewrite extra_data
  }
} catch (e) { /* network error handling */ }

Prevention

When it happens

Trigger: GET /openid/status when the authenticated user's auth_methods row has extra_data that is not parseable JSON (e.g. empty string, truncated JSON, or a plain string written by an older server version).

Common situations: Upgrading from a server version that stored extra_data in a non-JSON shape; manually editing the SQLite database; a failed/interrupted save of OpenID settings; restoring a budget/DB across incompatible schema versions.

Related errors


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