{"record":{"id":"bc54056174ced4e8","repo":"actualbudget/actual","slug":"error-bc5405","errorCode":null,"errorMessage":"${error}","messagePattern":"\\$\\{error\\}","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"packages/sync-server/src/app-openid.ts","lineNumber":42,"sourceCode":"  message: { status: 'error', reason: 'too-many-requests' },\n});\n\nexport { app as handlers, openIdConfigRateLimiter };\n\napp.post('/enable', validateSessionMiddleware, async (req, res) => {\n  if (!isAdmin(res.locals.user_id)) {\n    res.status(403).send({\n      status: 'error',\n      reason: 'forbidden',\n      details: 'permission-not-found',\n    });\n    return;\n  }\n\n  const { error } = (await enableOpenID(req.body)) || {};\n\n  if (error) {\n    res.status(500).send({ status: 'error', reason: error });\n    return;\n  }\n  res.send({ status: 'ok' });\n});\n\napp.post('/disable', validateSessionMiddleware, async (req, res) => {\n  if (!isAdmin(res.locals.user_id)) {\n    res.status(403).send({\n      status: 'error',\n      reason: 'forbidden',\n      details: 'permission-not-found',\n    });\n    return;\n  }\n\n  const { error } = (await disableOpenID(req.body)) || {};\n\n  if (error) {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-openid.ts#L24-L60","documentation":"When POST /openid passes the admin check, it calls enableOpenID(req.body). If that function returns an error, the endpoint responds 500 with reason set to the returned error string. The message is dynamic (the raw error from enabling OpenID), so its exact text depends on what enableOpenID rejected — commonly invalid OpenID configuration discovery or issuer issues.","triggerScenarios":"enableOpenID fails — e.g. the provided OpenID discovery URL is unreachable, the issuer metadata cannot be fetched/parsed, or the configuration body is invalid — causing the route to send { status:'error', reason: error } with HTTP 500.","commonSituations":"Entering a wrong OpenID issuer/discovery URL when configuring server authentication; OpenID provider downtime; network egress blocked from the sync-server to the IdP; malformed request body missing required fields.","solutions":["Read the `reason` field in the 500 response — it contains the underlying enableOpenID error","Verify the OpenID discovery URL/issuer is correct and reachable from the server","Ensure the request body contains all required OpenID configuration fields","Check server logs for the full stack behind the error"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// pre-check discovery URL reachability\nconst res = await fetch(discoveryUrl + '/.well-known/openid-configuration');\nif (!res.ok) throw new Error('OpenID discovery URL unreachable before enabling');","typeGuard":"null","tryCatchPattern":"const { error } = await enableOpenId(body);\nif (error) {\n  logger.error('enableOpenID failed:', error);\n  // fix config per the returned reason, then retry\n}","preventionTips":["Validate the issuer/discovery URL before submitting","Confirm the sync-server can reach the IdP (network egress, TLS)","Send the complete expected body shape","Read the response `reason` field — it names the exact failure"],"tags":["openid","http-500","configuration","discovery"],"backgroundTag":"openid-provider-configuration-error","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}