Budibase/budibase · error
User ID missing
Error message
User ID missing
What it means
assignExistingUsersToWorkspace adds existing users to workspace roles/groups. Before calling groupStore.addUser(groupId, user._id) it asserts user._id is present. A user entry lacking an _id cannot be referenced by group APIs, so it throws 'User ID missing'. It guards against malformed user objects returned by the bulk-save flow.
Source
Thrown at packages/builder/src/settings/pages/people/users/workspaceInviteUtils.ts:269
shouldSyncGlobalRole(selectedRole, user))
) {
const loaded = await users.get(user._id)
if (loaded) {
fullUser = loaded
rev = loaded._rev
}
}
if (
user._id &&
fullUser &&
shouldSyncGlobalRole(selectedRole, fullUser)
) {
const roleUpdates = getRoleFlags(selectedRole, fullUser)
const saved = await users.save({ ...fullUser, ...roleUpdates })
rev = saved?._rev || rev
}
if (!user._id) {
throw new Error("User ID missing")
}
if (groupIdsToAdd.length) {
await Promise.all(
groupIdsToAdd.map(groupId => groupStore.addUser(groupId, user._id!))
)
}
if (!role) {
return email
}
if (groupIdsToAdd.length || !rev) {
const loaded = await users.get(user._id)
rev = loaded?._rev
}
if (!user._id || !rev) {
throw new Error("User ID or revision missing")
}
await users.addUserToWorkspace(user._id, role, rev)
return emailView on GitHub (pinned to a81a902e9a)
Solutions
- Ensure each user passed to assignExistingUsersToWorkspace is a fully fetched existing user with _id (use users.get by email/id first).
- Filter out users without _id before assignment and route them through the create/invite flow instead.
- Refresh the user list before assignment to remove stale/partial records.
Example fix
// before users.forEach(u => assign(u)) // after const assignable = users.filter(u => !!u._id) assignable.forEach(u => assign(u))
Defensive patterns
Strategy: validation
Validate before calling
const missing = selectedUsers.filter(u => !u._id)
if (missing.length) {
// resolve via users.get by email or route to invite flow
await Promise.all(missing.map(u => resolveOrCreateUser(u.email)))
} Type guard
const isExistingUser = (u: Partial<User>): u is User & { _id: string } =>
typeof u._id === "string" && u._id.length > 0 Try / catch
try {
await assignExistingUsersToWorkspace({ users, ... })
} catch (e) {
if (e.message === "User ID missing") {
// fall back to create/invite flow for entries without ids
return inviteNewUsers(users.filter(u => !u._id))
}
throw e
} Prevention
- Filter user lists to records with _id before workspace assignment.
- Route email-only entries through the create/invite flow instead.
- Refresh user data from the server rather than reusing stale client-side objects.
When it happens
Trigger: Processing a selected user in assignExistingUsersToWorkspace whose object has no _id — e.g. the user record was constructed client-side from an email-only entry, or a partial object from a search/list response was passed instead of a full fetched user.
Common situations: Inviting via pasted emails where the email does not match an existing user so no _id was resolved; stale cached user lists; mixing 'existing user' and 'new invite' paths so an invite-only entry reaches the existing-user branch.
Related errors
- User ID or revision missing
- User revision missing
- Query ID or Revision is missing
- Unable to retrieve prod DB - no workspace ID.
- Unable to retrieve dev DB - no workspace ID.
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/3ad0b568a358be52.
Report an issue: GitHub.