{"record":{"id":"add1544ea833ff23","repo":"actualbudget/actual","slug":"invalid-prefs","errorCode":"invalid-prefs","errorMessage":"invalid-prefs","messagePattern":"invalid-prefs","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/sync-server/src/app-account.js","lineNumber":179,"sourceCode":"});\n\napp.post('/server-prefs', (req, res) => {\n  const session = validateSession(req, res);\n  if (!session) return;\n\n  if (!isAdmin(session.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 { prefs } = req.body || {};\n\n  if (!prefs || typeof prefs !== 'object') {\n    res.status(400).send({ status: 'error', reason: 'invalid-prefs' });\n    return;\n  }\n\n  setServerPrefs(prefs);\n\n  res.send({ status: 'ok', data: {} });\n});\n\napp.get('/validate', (req, res) => {\n  const session = validateSession(req, res);\n  if (session) {\n    const user = getUserInfo(session.user_id);\n    if (!user) {\n      res.status(400).send({ status: 'error', reason: 'User not found' });\n      return;\n    }\n\n    res.send({","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-account.js#L161-L197","documentation":"HTTP 400 from POST /server-prefs with `reason:'invalid-prefs'`. After the admin check, the endpoint destructures `prefs` from the body and requires it to be a truthy object; otherwise it rejects the request without calling `setServerPrefs`. This is a server-side schema validation for the prefs payload.","triggerScenarios":"POST /server-prefs with a body missing the `prefs` key, `prefs: null`, `prefs` as a string/number/array-like, or a body that fails to parse so `req.body` is undefined (`req.body || {}` yields no prefs).","commonSituations":"Client sends `{}` instead of `{prefs:{...}}`; sends the prefs object at the top level; Content-Type not application/json so req.body is empty; automation sending form-encoded data.","solutions":["Wrap the settings in a `prefs` key: `{\"prefs\": {\"key\": \"value\"}}` with Content-Type application/json.","Validate on the client that `typeof prefs === 'object' && prefs !== null` before calling the endpoint.","If req.body is empty, fix the request encoding/Content-Type so express.json() parses it."],"exampleFix":"// before\nawait api.post('/server-prefs', { 'cloudFileId': 'x' });\n// after\nawait api.post('/server-prefs', { prefs: { 'cloudFileId': 'x' } });","handlingStrategy":"validation","validationCode":"function isValidPrefsPayload(body) {\n  return body != null && typeof body.prefs === 'object' && body.prefs !== null && !Array.isArray(body.prefs);\n}\nif (!isValidPrefsPayload({ prefs })) throw new Error('POST /server-prefs requires body { prefs: object }');","typeGuard":"function isPrefsBody(b) {\n  return typeof b === 'object' && b !== null && 'prefs' in b && typeof b.prefs === 'object';\n}","tryCatchPattern":"try {\n  await post('/server-prefs', { prefs }, jsonHeaders);\n} catch (e) {\n  if (e.response?.data?.reason === 'invalid-prefs') throw new PayloadError('prefs must be a non-null object');\n  throw e;\n}","preventionTips":["Always nest settings under a `prefs` key","Serialize with JSON and set Content-Type: application/json","Unit-test request payloads against a shared schema with the client"],"tags":["http-400","validation","schema","server-prefs"],"backgroundTag":"schema-validation-failed","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}