Budibase/budibase · error
Replacing members is not allowed
Error message
Replacing members is not allowed
What it means
SCIM group member updates support "add" and "remove" operations only. When a PATCH operation has op "replace" (or "Replace") targeting members, update throws this error because replacing the entire member list is not a supported mutation in this implementation. The switch then falls through to utils.unreachable for unknown ops.
Source
Thrown at packages/worker/src/api/controllers/global/scim/groups.ts:136
for (const u of value) {
usersToAdd.push(await scimUsers.find(u.value))
}
break
case "remove":
case "Remove":
for (const u of value) {
try {
usersToRemove.push(await scimUsers.find(u.value))
} catch (e: any) {
if (e.status !== 404) {
throw e
}
}
}
break
case "replace":
case "Replace":
throw new Error("Replacing members is not allowed")
default:
utils.unreachable(op)
}
}
if (usersToAdd.length) {
await groups.addUsers(
id,
usersToAdd.map(u => u._id!)
)
}
if (usersToRemove.length) {
await groups.removeUsers(
id,
usersToRemove.map(u => u._id!)
)
}
}View on GitHub (pinned to a81a902e9a)
Solutions
- Reconfigure the identity provider SCIM app to send incremental add/remove member operations instead of full-membership replace
- Translate replace operations on the client into explicit add and remove operations per member
- If you control the server and need replace support, implement it by diffing current vs desired members and applying add/remove
- Check the IdP's SCIM connector settings (e.g. Okta 'Group Member' push mode) for a delta-sync option
Example fix
// before (client PATCH payload)
{ "Operations": [{ "op": "replace", "path": "members", "value": [...] }] }
// after
{ "Operations": [
{ "op": "add", "path": "members", "value": [{ "value": "userId1" }] },
{ "op": "remove", "path": "members[value eq \"userId2\"]" }
] } Defensive patterns
Strategy: validation
Validate before calling
const ops = patchBody.Operations || []
const hasReplace = ops.some(o => String(o.op).toLowerCase() === "replace" &&
(!o.path || o.path.startsWith("members")))
if (hasReplace) throw new Error("Convert replace member ops to add/remove before sending") Type guard
function isMemberReplaceOp(op: { op: string; path?: string }): boolean {
return op.op.toLowerCase() === "replace" && (!op.path || op.path === "members")
} Try / catch
try {
await scim.patchGroup(groupId, body)
} catch (e) {
if (e.message.includes("Replacing members")) {
// recompute diff and resend as add/remove operations
}
} Prevention
- Configure your IdP SCIM connector for delta/incremental group member sync
- Always translate desired membership to add/remove diffs client-side
- Test SCIM payloads against the server's supported operations before enabling sync
When it happens
Trigger: Sending a SCIM PATCH /Groups request whose Operations array contains {"op": "replace", "path": "members", ...} — typically from an SCIM client (Okta, Azure AD/Entra) configured to sync full group membership instead of incremental adds/removes.
Common situations: An identity provider SCIM integration set to "sync all members" mode sends replace ops; a client application builds PATCH payloads with replace instead of add/remove; SCIM spec-conformance tooling tests replace semantics.
Related errors
- Email is required
- Group name "${name}" is unavailable
- No user ID provided for getting
- Unable to delete self.
- User must be provided for password recovery.
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/8ddd8c0e2438fc41.
Report an issue: GitHub.