Mintplex-Labs/anything-llm · error
Invalid role. Allowed roles are
Error message
Invalid role. Allowed roles are: ${VALID_ROLES.join(", ")} What it means
Thrown by the role validator when a create or update receives a role not in the hardcoded VALID_ROLES list ("default", "admin", "manager"). Roles are validated before any database write and the message interpolates the allowed list. Forks or version drift that add or rename roles will trip this when the client sends a value the running server does not know. Passing role: undefined is safe (defaults to "default"), but any other unknown string throws.
Solutions
- Use one of exactly: default, admin, manager
- Map human-readable labels to machine values before calling the API
- Derive the allowed list from the server contract rather than hardcoding it in clients
- If a custom role is truly required, extend VALID_ROLES in server/models/user.js and redeploy
Example fix
// before
await user.update(userId, { role: "Administrator" });
// after
await user.update(userId, { role: "admin" }); Defensive patterns
Strategy: validation
Validate before calling
const VALID_ROLES = ["default", "admin", "manager"];
function isValidRole(role) {
return role == null || VALID_ROLES.includes(role);
}
if (!isValidRole(body.role)) {
return res.status(400).json({ error: `Invalid role. Allowed roles are: ${VALID_ROLES.join(", ")}` });
} Type guard
/** @param {unknown} v */
function isRole(v) {
return typeof v === "string" && ["default", "admin", "manager"].includes(v);
} Try / catch
try {
await user.update(userId, { role });
} catch (e) {
if (/Invalid role/i.test(e.message)) return res.status(400).json({ error: e.message });
throw e;
} Prevention
- Treat the role list as a versioned API contract; never hardcode in clients
- Send machine values, never display labels
- Guard scripts with an includes() check before promoting users
When it happens
Trigger: user.update(userId, { role: "owner" }) or User.create with role "superadmin"; an API client written against a fork with extra roles; sending a display label ("Administrator") instead of the machine value; role: "" from an empty form field.
Common situations: Version drift between the API client and server role list; custom forks adding roles then running upstream code; admin UI dropdown out of sync with backend; scripts promoting users with invented role names.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Bio cannot be longer than 1,000 characters
- Daily message limit must be null or a number greater than…
- Invalid device OS
- Invalid scope
- No valid updates provided.
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/4196e9fafd075fe6.
Report an issue: GitHub.
Appendix: source
Thrown at server/models/user.js:54
try {
const username = String(newValue);
if (username.length > 64)
throw new Error("Username cannot be longer than 64 characters");
if (username.length < 2)
throw new Error("Username must be at least 2 characters");
if (!User.usernameRegex.test(username))
throw new Error(
"Username must start with a lowercase letter and only contain lowercase letters, numbers, underscores, hyphens, and periods"
);
return username;
} catch (e) {
throw new Error(e.message);
}
},
role: (role = "default") => {
const VALID_ROLES = ["default", "admin", "manager"];
if (!VALID_ROLES.includes(role)) {
throw new Error(
`Invalid role. Allowed roles are: ${VALID_ROLES.join(", ")}`
);
}
return String(role);
},
dailyMessageLimit: (dailyMessageLimit = null) => {
if (dailyMessageLimit === null) return null;
const limit = Number(dailyMessageLimit);
if (isNaN(limit) || limit < 1) {
throw new Error(
"Daily message limit must be null or a number greater than or equal to 1"
);
}
return limit;
},
bio: (bio = "") => {
if (!bio || typeof bio !== "string") return "";
if (bio.length > 1000)View on GitHub (pinned to 3aec848f28)