Budibase/budibase · error · HTTPError

Password change is disabled for this user

Error message

Password change is disabled for this user

What it means

buildUser throws an HTTPError(400) when a caller attempts to set or change a password for a user whose account is SSO-managed (isPreventPasswordActions returns true). SSO-authenticated users must change passwords via their identity provider, so Budibase blocks local password writes. This protects consistency between the local CouchDB user record and the external IdP.

Source

Thrown at packages/backend-core/src/users/db.ts:133

    opts: SaveUserOpts = {
      hashPassword: true,
      requirePassword: true,
    },
    tenantId: string,
    dbUser?: any,
    account?: Account
  ): Promise<User> {
    let { password, _id } = user

    // don't require a password if the db user doesn't already have one
    if (dbUser && !dbUser.password) {
      opts.requirePassword = false
    }

    let hashedPassword
    if (password && password !== dbUser?.password) {
      if (await UserDB.isPreventPasswordActions(user, account)) {
        throw new HTTPError("Password change is disabled for this user", 400)
      }

      if (!opts.skipPasswordValidation) {
        const passwordValidation = validatePassword(password)
        if (!passwordValidation.valid) {
          throw new HTTPError(passwordValidation.error, 400)
        }
      }

      hashedPassword = opts.hashPassword ? await hash(password) : password
    } else if (dbUser) {
      hashedPassword = dbUser.password
    }

    // passwords are never required if sso is enforced
    const requirePasswords =
      opts.requirePassword && !(await UserDB.features.isSSOEnforced())
    if (!hashedPassword && requirePasswords) {

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Remove the password field from the update payload so the user keeps their existing/SSO-managed password
  2. Have the user change their password in the SSO identity provider instead
  3. Convert the user off SSO (or the tenant off SSO enforcement) before allowing local password changes
  4. Pass opts.skipPasswordValidation is irrelevant here; instead check UserDB.isPreventPasswordActions before sending a password

Example fix

// before
await users.save({ _id: userId, password: "newPass123" })
// after
// omit password for SSO users
const update: any = { _id: userId }
if (!(await UserDB.isPreventPasswordActions(user))) {
  update.password = "newPass123"
}
await users.save(update)
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check before saving
import { UserDB } from "@budibase/backend-core/users"
const blocked = await UserDB.isPreventPasswordActions(user, account)
if (blocked) throw new Error("User is SSO-managed; omit the password field")

Type guard

function isSsoManaged(user: { ssoId?: string }): boolean {
  return typeof user.ssoId === "string" && user.ssoId.length > 0
}

Try / catch

try {
  await users.save({ _id: userId, password: newPass })
} catch (e: any) {
  if (e?.status === 400 && e?.message === "Password change is disabled for this user") {
    // handle SSO-managed user: notify admin / redirect to IdP
  } else throw e
}

Prevention

When it happens

Trigger: Calling save/buildUser/bulkCreate (via builtUser) with a `password` field that differs from the stored password on a user whose account is flagged SSO (isSSOUser or the tenant's account is an SSO account matching the user email).

Common situations: Admins trying to reset an SSO user's password from the admin UI or API; sync scripts bulk-updating users with password fields; self-hosted configs where SSO was enabled after password users existed; passing the full user object back to the save endpoint (echoing a stale password) instead of omitting it.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/49c2bb7154fcef3d. Report an issue: GitHub.