{"record":{"id":"3ad0b568a358be52","repo":"Budibase/budibase","slug":"user-id-missing","errorCode":null,"errorMessage":"User ID missing","messagePattern":"User ID missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/builder/src/settings/pages/people/users/workspaceInviteUtils.ts","lineNumber":269,"sourceCode":"            shouldSyncGlobalRole(selectedRole, user))\n        ) {\n          const loaded = await users.get(user._id)\n          if (loaded) {\n            fullUser = loaded\n            rev = loaded._rev\n          }\n        }\n        if (\n          user._id &&\n          fullUser &&\n          shouldSyncGlobalRole(selectedRole, fullUser)\n        ) {\n          const roleUpdates = getRoleFlags(selectedRole, fullUser)\n          const saved = await users.save({ ...fullUser, ...roleUpdates })\n          rev = saved?._rev || rev\n        }\n        if (!user._id) {\n          throw new Error(\"User ID missing\")\n        }\n        if (groupIdsToAdd.length) {\n          await Promise.all(\n            groupIdsToAdd.map(groupId => groupStore.addUser(groupId, user._id!))\n          )\n        }\n        if (!role) {\n          return email\n        }\n        if (groupIdsToAdd.length || !rev) {\n          const loaded = await users.get(user._id)\n          rev = loaded?._rev\n        }\n        if (!user._id || !rev) {\n          throw new Error(\"User ID or revision missing\")\n        }\n        await users.addUserToWorkspace(user._id, role, rev)\n        return email","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/builder/src/settings/pages/people/users/workspaceInviteUtils.ts#L251-L287","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nusers.forEach(u => assign(u))\n// after\nconst assignable = users.filter(u => !!u._id)\nassignable.forEach(u => assign(u))","handlingStrategy":"validation","validationCode":"const missing = selectedUsers.filter(u => !u._id)\nif (missing.length) {\n  // resolve via users.get by email or route to invite flow\n  await Promise.all(missing.map(u => resolveOrCreateUser(u.email)))\n}","typeGuard":"const isExistingUser = (u: Partial<User>): u is User & { _id: string } =>\n  typeof u._id === \"string\" && u._id.length > 0","tryCatchPattern":"try {\n  await assignExistingUsersToWorkspace({ users, ... })\n} catch (e) {\n  if (e.message === \"User ID missing\") {\n    // fall back to create/invite flow for entries without ids\n    return inviteNewUsers(users.filter(u => !u._id))\n  }\n  throw e\n}","preventionTips":["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."],"tags":["users","workspace","missing-id","builder"],"backgroundTag":"missing-user-id","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}