payloadcms/payload · error · Error
Email or username is required.
Error message
Email or username is required.
What it means
Thrown in the server-function `login` when the collection uses `loginWithUsername` with `allowEmailLogin: true` and neither `email` nor `username` is supplied. Payload cannot identify the account with zero identifiers. Plain `Error` (no HTTP status).
Source
Thrown at packages/payload/src/auth/serverFunctions/login.ts:56
email,
password,
serverAdapter,
username,
}: LoginArgs<TSlug>): Promise<LoginResult<TSlug>> {
const payload = await getPayload({ config, cron: true })
const authConfig = payload.collections[collection]?.config.auth
if (!authConfig) {
throw new Error(`No auth config found for collection: ${collection}`)
}
const loginWithUsername = authConfig.loginWithUsername ?? false
if (loginWithUsername) {
if (loginWithUsername.allowEmailLogin) {
if (!email && !username) {
throw new Error('Email or username is required.')
}
} else {
if (!username) {
throw new Error('Username is required.')
}
}
} else {
if (!email) {
throw new Error('Email is required.')
}
}
let loginData
if (loginWithUsername) {
loginData = username ? { password, username } : { email, password }
} else {
loginData = { email, password }View on GitHub (pinned to 00c58b35c0)
Solutions
- Pass exactly one identifier: `login({ collection, password, email, serverAdapter })` or `login({ collection, password, username, serverAdapter })`.
- Validate that at least one of email/username is non-empty before calling.
- Respect the `LoginArgs` union type (`{ email } | { username }`) so TypeScript enforces the either/or.
Example fix
// before
await login({ collection, password, serverAdapter })
// after
await login({ collection, password, email: identifier, serverAdapter }) Defensive patterns
Strategy: validation
Validate before calling
// Ensure exactly one identifier is provided
if (!email && !username) {
throw new Error('Email or username is required')
}
await login({
collection,
config,
password,
...(email ? { email } : { username }),
serverAdapter,
}) Type guard
function hasLoginIdentifier(args: { email?: string; username?: string }): boolean {
return !!args.email || !!args.username
} Try / catch
// Plain Error (not APIError) — validate before calling instead
if (!email && !username) {
// prompt the user for an identifier
} else {
await login({ collection, config, password, ...(email ? { email } : { username }), serverAdapter })
} Prevention
- Respect the `LoginArgs` union type (`{ email } | { username }`) — avoid `as any`.
- Validate at least one identifier is non-empty before calling.
- Submit the identifier the user actually entered from the form.
When it happens
Trigger: Calling `login({ collection, password, serverAdapter })` without `email` or `username`; both fields are empty strings; the adapter's caller destructured only `password`. With `allowEmailLogin`, exactly one of email/username is required.
Common situations: A login form that conditionally renders email/username but submits when both are blank; the union type `LoginArgs` is bypassed by a `as any` cast; the frontend forgot to include the identifier field in the POST body that the adapter reads.
Related errors
- validation:required
- No auth config found for collection: ${collection}
- Username is required.
- Email is required.
- Username or email is required
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/046c35855ce3a3f7.
Report an issue: GitHub.