{"record":{"id":"695fac3836827f78","repo":"actualbudget/actual","slug":"invalid-openid-configuration","errorCode":null,"errorMessage":"Invalid OpenID configuration","messagePattern":"Invalid OpenID configuration","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"packages/sync-server/src/app-openid.ts","lineNumber":94,"sourceCode":"    res.status(400).send({ status: 'error', reason: 'invalid-password' });\n    return;\n  }\n\n  const auth = UserService.getOpenIDConfig();\n\n  if (!auth) {\n    res\n      .status(500)\n      .send({ status: 'error', reason: 'OpenID configuration not found' });\n    return;\n  }\n\n  try {\n    const openIdConfig = JSON.parse(auth.extra_data);\n    res.send({ status: 'ok', data: { openId: openIdConfig } });\n  } catch {\n    res\n      .status(500)\n      .send({ status: 'error', reason: 'Invalid OpenID configuration' });\n  }\n});\n\napp.get('/callback', async (req, res) => {\n  const { error, url } = await loginWithOpenIdFinalize(req.query);\n\n  if (error) {\n    res.status(400).send({ status: 'error', reason: error });\n    return;\n  }\n\n  if (!isValidRedirectUrl(url)) {\n    res.status(400).send({ status: 'error', reason: 'Invalid redirect URL' });\n    return;\n  }\n\n  res.redirect(url);","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-openid.ts#L76-L112","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Re-save the OpenID configuration in the server settings so extra_data is rewritten as valid JSON","Inspect the auth_methods table row and fix or delete the corrupt extra_data value","Delete the affected auth method and re-add the OpenID provider","Check server release notes for migrations that convert extra_data format and re-run them"],"exampleFix":"// before (corrupt row)\nextra_data = 'issuer=https://id.example.com'\n// after\nextra_data = '{\"issuer\":\"https://id.example.com\",\"client_id\":\"...\",\"client_secret\":\"...\"}'","handlingStrategy":"validation","validationCode":"function isOpenIdConfigData(extraData) {\n  try {\n    const cfg = JSON.parse(extraData);\n    return cfg && typeof cfg === 'object' && typeof cfg.issuer === 'string';\n  } catch { return false; }\n}\n// call before relying on the /openid/status response","typeGuard":"function isOpenIdConfig(v) {\n  return v !== null && typeof v === 'object' && 'issuer' in v;\n}","tryCatchPattern":"try {\n  const res = await fetch('/openid/status');\n  const body = await res.json();\n  if (body.status === 'error' && body.reason === 'Invalid OpenID configuration') {\n    await reSaveOpenIdConfig(); // rewrite extra_data\n  }\n} catch (e) { /* network error handling */ }","preventionTips":["Never hand-edit extra_data in the database","Re-save OpenID settings after server upgrades","Validate stored JSON parses before saving it","Back up the DB before schema/migration work"],"tags":["openid","http-500","config","json-parse"],"backgroundTag":"invalid-json-configuration","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}