actualbudget/actual · error
user-cant-be-empty
user-cant-be-empty
Error message
Username cannot be empty
What it means
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`.)
Source
Thrown at packages/sync-server/src/app-admin.js:60
enabled: u.enabled === 1,
})),
);
});
app.post('/users', validateSessionMiddleware, async (req, res) => {
if (!isAdmin(res.locals.user_id)) {
res.status(403).send({
status: 'error',
reason: 'forbidden',
details: 'permission-not-found',
});
return;
}
const { userName, role, displayName, enabled } = req.body || {};
if (!userName || !role) {
res.status(400).send({
status: 'error',
reason: `${!userName ? 'user-cant-be-empty' : 'role-cant-be-empty'}`,
details: `${!userName ? 'Username' : 'Role'} cannot be empty`,
});
return;
}
const roleIdFromDb = UserService.validateRole(role);
if (!roleIdFromDb) {
res.status(400).send({
status: 'error',
reason: 'role-does-not-exists',
details: 'Selected role does not exist',
});
return;
}
const userIdInDb = UserService.getUserByUsername(userName);View on GitHub (pinned to d4334cb6e6)
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.
Example fix
// before
await api.post('/users', { username: name, role: 'basic' });
// after
await api.post('/users', { userName: name, role: 'basic' }); Defensive patterns
Strategy: validation
Validate before calling
function canCreateUser(body) {
return typeof body?.userName === 'string' && body.userName.trim() !== ''
&& typeof body?.role === 'string' && body.role.trim() !== '';
}
if (!canCreateUser(payload)) throw new Error('userName and role are required'); Type guard
function hasRequiredUserFields(b) {
return typeof b === 'object' && b !== null
&& typeof b.userName === 'string' && b.userName.length > 0
&& typeof b.role === 'string' && b.role.length > 0;
} Try / catch
try {
await post('/users', { userName, role }, { headers: authHeaders(adminToken) });
} catch (e) {
if (e.response?.data?.reason === 'user-cant-be-empty') throw new ValidationError('userName is required');
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- "${field}" is required for table "${table}": ${JSON.stringif
- Invalid user IDs
- invalid-prefs
- role-does-not-exists
- invalid-file-id
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/80efae5feb3af0dc.
Report an issue: GitHub.