import type { TFunction } from '@payloadcms/translations' import { en } from '@payloadcms/translations/languages/en' import type { LabelFunction, StaticLabel } from '../config/types.js' import type { PayloadRequest } from '../types/index.js' import { httpStatus } from '../utilities/httpStatus.js' import { APIError } from './APIError.js' /** @deprecated Use `instanceof ValidationError` instead of name comparison. */ export const ValidationErrorName = 'ValidationError' export type ValidationFieldError = { /** Configured field label, when available. */ label?: LabelFunction | StaticLabel /** Locale for a localized validation pass. Omitted for non-localized validation. */ locale?: string /** Error message to display for this field. */ message: string /** Dot-separated path to the invalid field. */ path: string /** Database table associated with the invalid field, when applicable. */ tableName?: string } export class ValidationError extends APIError<{ collection?: string errors: ValidationFieldError[] global?: string }> { constructor( results: { collection?: string errors: ValidationFieldError[] global?: string id?: number | string /** * req needs to be passed through (if you have one) in order to resolve label functions that may be part of the errors array */ req?: Partial }, t?: TFunction, ) { const message = t ? t('error:followingFieldsInvalid', { count: results.errors.length }) : results.errors.length === 1 ? en.translations.error.followingFieldsInvalid_one : en.translations.error.followingFieldsInvalid_other const req = results.req // delete to avoid logging the whole req delete results['req'] const locales = results.errors .map((fieldError) => fieldError.locale) .filter((locale): locale is string => Boolean(locale)) const spansMultipleLocales = new Set(locales).size > 1 super( `${message} ${results.errors .map((f) => { const localePrefix = spansMultipleLocales && f.locale ? `[${f.locale}] ` : '' if (f.label) { if (typeof f.label === 'function') { if (!req || !req.i18n || !req.t) { return `${localePrefix}${f.path}` } return `${localePrefix}${f.label({ i18n: req.i18n, t: req.t })}` } if (typeof f.label === 'object') { if (req?.i18n?.language) { return `${localePrefix}${f.label[req.i18n.language]}` } return `${localePrefix}${f.label[Object.keys(f.label)[0]!]}` } return `${localePrefix}${f.label}` } return `${localePrefix}${f.path}` }) .join(', ')}`, httpStatus.BAD_REQUEST, results, ) this.name = 'ValidationError' } }