Budibase/budibase · error

Unable to create new user, invitation invalid.

Error message

Unable to create new user, invitation invalid.

What it means

The worker's invite-users handler catches errors thrown while creating users/invitations and re-throws them as a 400, preferring the underlying error message and only falling back to 'Unable to create new user, invitation invalid.' if none exists. This generic fallback appears when the failure produced no message at all. It signals the invite or user-creation step failed (duplicate email, quota exceeded re-thrown above this branch, DB error, etc.).

Source

Thrown at packages/worker/src/api/controllers/global/users.ts:776

          expires: new Date(0),
        })

        ctx.body = {
          _id: user._id!,
          _rev: user._rev!,
          email: user.email,
          tenantId: user.tenantId,
        }
      }
    )
  } catch (err: any) {
    if (err.code === APIWarningCode.USAGE_LIMIT_EXCEEDED) {
      // explicitly re-throw limit exceeded errors
      ctx.throw(400, err?.message || err)
    }
    console.warn("Error inviting user", err)
    ctx.throw(
      400,
      err?.message || err || "Unable to create new user, invitation invalid."
    )
  }
}

export const addUserToWorkspace = async (
  ctx: UserCtx<
    EditUserPermissionsResponse,
    SaveUserResponse,
    { userId: string; role: string }
  >
) => handleUserWorkspacePermission(ctx, ctx.params.userId, ctx.params.role)

export const removeUserFromWorkspace = async (
  ctx: UserCtx<
    EditUserPermissionsResponse,
    SaveUserResponse,
    { userId: string }

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Inspect worker logs for 'Error inviting user' to get the real underlying error object.
  2. Validate the invite payload: emails are valid addresses and rows are not duplicated within the batch.
  3. Retry after confirming the invite cache and CouchDB are healthy.
  4. If a usage limit was actually hit, the endpoint returns the specific USAGE_LIMIT_EXCEEDED message instead — check billing/plan limits separately.

Example fix

// before: posting rows with empty email strings
await api.post('/api/global/users/invite', rows)
// after: filter invalid rows client-side first
const valid = rows.filter(r => /^[^@]+@[^@]+\.[^@]+$/.test(r.email))
await api.post('/api/global/users/invite', valid)
Defensive patterns

Strategy: validation

Validate before calling

const valid = invites.filter(i => /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(i.email))
if (valid.length === 0) throw new Error("No valid emails to invite")

Type guard

const isInvitable = (u: unknown): u is { email: string } =>
  typeof u === "object" && u !== null && "email" in u && typeof (u as { email: unknown }).email === "string"

Try / catch

try {
  await api.inviteUsers(invites)
} catch (e) {
  // prefer e.message; fallback is generic invite failure
  console.error(e?.message || "Unable to create new user, invitation invalid.")
}

Prevention

When it happens

Trigger: POST to the invite users endpoint where userSdk.invite throws a non-usage-limit error with no message, e.g. invitation cache write fails, payload malformed so err?.message and err are both falsy after handling.

Common situations: Bulk CSV import containing rows that all fail validation leaving an empty error; invite storage (CouchDB/Redis) unavailable; tenant user quota checks throwing empty errors; stale client SDKs posting a legacy payload shape.

Related errors


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