{"record":{"id":"80efae5feb3af0dc","repo":"actualbudget/actual","slug":"user-cant-be-empty","errorCode":"user-cant-be-empty","errorMessage":"Username cannot be empty","messagePattern":"Username cannot be empty","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/sync-server/src/app-admin.js","lineNumber":60,"sourceCode":"      enabled: u.enabled === 1,\n    })),\n  );\n});\n\napp.post('/users', 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 { 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);","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/sync-server/src/app-admin.js#L42-L78","documentation":"HTTP 400 from POST /users with `reason:'user-cant-be-empty', details:'Username cannot be empty'`. After the admin check, the endpoint requires both `userName` and `role` in the body; when `userName` is falsy it rejects with this dedicated validation reason. (An empty `role` produces the sibling `role-cant-be-empty`.)","triggerScenarios":"POST /users by an admin where `userName` is missing, empty string, null, or undefined — e.g. body `{}`, `{role:'basic'}`, or a body that failed JSON parsing so destructuring yields undefined.","commonSituations":"Scripts building the payload from an unset environment variable (`userName: process.env.NEW_USER` when unset); clients sending the field under a different name (`username`, `name`); empty Content-Type bodies.","solutions":["Include a non-empty `userName` string in the JSON body: `{\"userName\":\"alice\",\"role\":\"basic\"}`.","Check for field-name typos — the API expects exactly `userName` (camelCase), not `username` or `user_name`.","Client-side: guard `if (!userName) throw ...` before calling the endpoint to fail fast with a clearer message."],"exampleFix":"// before\nawait api.post('/users', { username: name, role: 'basic' });\n// after\nawait api.post('/users', { userName: name, role: 'basic' });","handlingStrategy":"validation","validationCode":"function canCreateUser(body) {\n  return typeof body?.userName === 'string' && body.userName.trim() !== ''\n    && typeof body?.role === 'string' && body.role.trim() !== '';\n}\nif (!canCreateUser(payload)) throw new Error('userName and role are required');","typeGuard":"function hasRequiredUserFields(b) {\n  return typeof b === 'object' && b !== null\n    && typeof b.userName === 'string' && b.userName.length > 0\n    && typeof b.role === 'string' && b.role.length > 0;\n}","tryCatchPattern":"try {\n  await post('/users', { userName, role }, { headers: authHeaders(adminToken) });\n} catch (e) {\n  if (e.response?.data?.reason === 'user-cant-be-empty') throw new ValidationError('userName is required');\n  throw e;\n}","preventionTips":["Use exactly `userName` and `role` (camelCase) in the payload","Check environment variables for user-provisioning scripts before sending","Validate payloads against a shared schema in the client before POSTing"],"tags":["http-400","validation","required-field","user-management"],"backgroundTag":"missing-required-field","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}