{"record":{"id":"d61797e0dc0e3a0d","repo":"actualbudget/actual","slug":"role-does-not-exists","errorCode":"role-does-not-exists","errorMessage":"Selected role does not exist","messagePattern":"Selected role does not exist","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/sync-server/src/app-admin.js","lineNumber":70,"sourceCode":"      details: 'permission-not-found',\n    });\n    return;\n  }\n\n  const { userName, role, displayName, enabled } = req.body || {};\n\n  if (!userName || !role) {\n    res.status(400).send({\n      status: 'error',\n      reason: `${!userName ? 'user-cant-be-empty' : 'role-cant-be-empty'}`,\n      details: `${!userName ? 'Username' : 'Role'} cannot be empty`,\n    });\n    return;\n  }\n\n  const roleIdFromDb = UserService.validateRole(role);\n  if (!roleIdFromDb) {\n    res.status(400).send({\n      status: 'error',\n      reason: 'role-does-not-exists',\n      details: 'Selected role does not exist',\n    });\n    return;\n  }\n\n  const userIdInDb = UserService.getUserByUsername(userName);\n  if (userIdInDb) {\n    res.status(400).send({\n      status: 'error',\n      reason: 'user-already-exists',\n      details: `User ${userName} already exists`,\n    });\n    return;\n  }\n\n  const userId = uuidv4();","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-admin.js#L52-L88","documentation":"The POST /users admin handler rejects a user-creation request whose `role` field does not map to a known role. `UserService.validateRole(role)` returns a falsy role id when the role name is not in the server's role table, so the handler responds 400 with reason 'role-does-not-exists'. This prevents accounts from being created with unusable permissions.","triggerScenarios":"POST /users (admin session) with body role set to a string not accepted by UserService.validateRole, e.g. role: 'superadmin', role: '' spelled differently than the canonical role names ('admin'/'basic'/'user' in Actual's sync-server), or a numeric role id passed instead of the name.","commonSituations":"Deploying an older sync-server that lacks a newer role name; custom admin dashboards or scripts sending hand-rolled role values; copying role strings from a different product's API; typos like 'Admin' vs 'admin' if the lookup is case-sensitive.","solutions":["Use a canonical role name accepted by validateRole (e.g. 'admin' or 'basic') in the POST /users body.","Check the role list in the running server version (packages/sync-server/src/util/roles.js) — the role may not exist in your release.","If the role genuinely should exist, upgrade the sync-server to a version that includes it.","Normalize case/whitespace on the client before sending the role value."],"exampleFix":"// before\nawait fetch('/users', { method: 'POST', body: JSON.stringify({ userName: 'jane', role: 'SuperAdmin', password: '...' }) });\n// after\nawait fetch('/users', { method: 'POST', body: JSON.stringify({ userName: 'jane', role: 'admin', password: '...' }) });","handlingStrategy":"validation","validationCode":"const VALID_ROLES = ['admin', 'basic'];\nfunction isValidRole(role) {\n  return typeof role === 'string' && VALID_ROLES.includes(role.trim().toLowerCase());\n}\nif (!isValidRole(role)) throw new Error(`Invalid role: ${role}`);","typeGuard":"function isKnownRole(r: unknown): r is 'admin' | 'basic' {\n  return r === 'admin' || r === 'basic';\n}","tryCatchPattern":"try {\n  const res = await fetch(base + '/users', { method: 'POST', ... });\n  const body = await res.json();\n  if (body.reason === 'role-does-not-exists') {\n    // fall back to a default known role and retry once\n  }\n} catch (e) { /* network failure */ }","preventionTips":["Keep the role list in sync with the deployed sync-server version.","Always send lowercase canonical role names from a fixed UI dropdown.","Add a pre-flight unit test asserting role strings against the server's roles module.","Trim and lowercase user-supplied role input before sending."],"tags":["http-400","validation","sync-server","admin-api"],"backgroundTag":"invalid-enum-value","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}