{"record":{"id":"4719db03458c8b94","repo":"actualbudget/actual","slug":"invalid-password","errorCode":"invalid-password","errorMessage":"invalid-password","messagePattern":"invalid-password","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/sync-server/src/app-openid.ts","lineNumber":76,"sourceCode":"  const { error } = (await disableOpenID(req.body)) || {};\n\n  if (error) {\n    res.status(401).send({ status: 'error', reason: error });\n    return;\n  }\n  res.send({ status: 'ok' });\n});\n\napp.post('/config', openIdConfigRateLimiter, async (req, res) => {\n  const ownerCount = UserService.getOwnerCount();\n\n  if (ownerCount > 0) {\n    res.status(400).send({ status: 'error', reason: 'already-bootstraped' });\n    return;\n  }\n\n  if (!(await checkPassword(req.body.password))) {\n    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)","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-openid.ts#L58-L94","documentation":"During OpenID bootstrapping via POST /openid/config, the submitted password is checked against the server's expected password (checkPassword). If it does not match, the endpoint responds 400 with reason 'invalid-password'. This protects the bootstrap step so only someone knowing the server password can configure OpenID.","triggerScenarios":"Calling POST /openid/config on an un-bootstrapped server with a `password` value that fails checkPassword (wrong or missing password in the body).","commonSituations":"Typos or wrong environment's password in provisioning scripts; omitting the password field entirely; password set via SERVER_PASSWORD env not matching what the script sends.","solutions":["Send the correct server password in the request body","Check the SERVER_PASSWORD env var / deployment config for the actual expected password","Ensure the password field is present and not empty in the JSON body","If the password is unknown, reset the server data to re-bootstrap from scratch"],"exampleFix":"// before\n{ \"openId\": { ... } }                       // 400 invalid-password\n// after\n{ \"password\": \"correct-server-password\", \"openId\": { ... } }","handlingStrategy":"validation","validationCode":"if (typeof password !== 'string' || password.length === 0) {\n  throw new Error('a non-empty server password is required for bootstrap');\n}","typeGuard":"function isBootstrapBody(b: unknown): b is { password: string } {\n  return typeof b === 'object' && b !== null &&\n    typeof (b as { password?: unknown }).password === 'string' &&\n    (b as { password: string }).password.length > 0;\n}","tryCatchPattern":"if (res.status === 400 && (await res.json()).reason === 'invalid-password') {\n  throw new Error('server password rejected — check SERVER_PASSWORD / deployment config');\n}","preventionTips":["Source the password from the same secret store the server uses (e.g. SERVER_PASSWORD)","Never hardcode passwords in provisioning scripts","Confirm the target environment before sending credentials","Fail fast with a clear message when the password field would be empty"],"tags":["authentication","http-400","openid","password","bootstrap"],"backgroundTag":"invalid-credentials","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}