payloadcms/payload · error · ValidationError

File type ${mimeTypeFromExtension} (from extension ${typeFro

Error message

File type ${mimeTypeFromExtension} (from extension ${typeFromExtension}) is not allowed. / SVG file contains potentially harmful content. / Invalid or corrupted PDF file. / Invalid PDF file. / Invalid MIME type: ${detected.mime}. / File type '${file.mimetype}' not allowed ${file.name}: Restricted file type detected -- set 'allowRestrictedFileTypes' to true to skip this check for this Collection.

What it means

Aggregate validation error thrown at the end of checkFileRestrictions when one or more content-level checks failed and were pushed into the errors[] array. Possible messages: (1) 'File type X (from extension Y) is not allowed.' — detected type was undefined and the extension's fallback MIME failed validateMimeType; (2) 'SVG file contains potentially harmful content.' — validateSvg flagged scripts/external refs; (3) 'Invalid or corrupted PDF file.' — extension-based PDF failed validatePDF; (4) 'Invalid PDF file.' — detected PDF failed validatePDF; (5) 'Invalid MIME type: <mime>.' — a type WAS detected but doesn't match upload.mimeTypes; (6) restricted-type message when no mimeTypes are configured and the file matches the blocklist. All collected errors are joined with ', ' in the final message.

Source

Thrown at packages/payload/src/uploads/checkFileRestrictions.ts:222

    if (detected && !passesMimeTypeCheck) {
      errors.push(`Invalid MIME type: ${detected.mime}.`)
    }
  } else {
    const isRestricted = RESTRICTED_FILE_EXT_AND_TYPES.some((type) => {
      const hasRestrictedExt = type.extensions.some((ext) => file.name.toLowerCase().endsWith(ext))
      const hasRestrictedMime = type.mimeType === file.mimetype
      return hasRestrictedExt || hasRestrictedMime
    })
    if (isRestricted) {
      errors.push(
        `File type '${file.mimetype}' not allowed ${file.name}: Restricted file type detected -- set 'allowRestrictedFileTypes' to true to skip this check for this Collection.`,
      )
    }
  }

  if (errors.length > 0) {
    req.payload.logger.error(errors.join(', '))
    throw new ValidationError({
      errors: [{ message: errors.join(', '), path: 'file' }],
    })
  }
}

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Match the file's real content type: add the detected MIME to upload.mimeTypes, or have the user upload a non-spoofed file.
  2. For SVGs, strip <script>, on* handlers, and external references before upload, or disable SVG uploads.
  3. For PDFs, ensure the upload completed (re-upload a non-corrupt file); validate with a PDF tool client-side.
  4. If you must accept a restricted/spoofed type, set upload.allowRestrictedFileTypes: true (skips checks) — understand the security implications first.
  5. Read the full joined message to identify which of the six sub-checks fired before changing config.

Example fix

// before — SVG with inline script rejected
upload: { staticDir: 'media', mimeTypes: ['image/svg+xml'] }
// after — sanitize SVGs upstream so validateSvg passes, or allow restricted types
upload: {
  staticDir: 'media',
  mimeTypes: ['image/svg+xml'],
  // only if you accept the risk:
  // allowRestrictedFileTypes: true,
}
Defensive patterns

Strategy: validation

Validate before calling

import { validateSvg } from 'payload/uploads/validateSvg' // replicate logic if private
import { validatePDF } from 'payload/uploads/validatePDF'
async function preflightFile(buf: Buffer, ext: string): Promise<string[]> {
  const errs: string[] = []
  if (ext.toLowerCase() === 'svg' && !validateSvg(buf)) errs.push('SVG unsafe')
  if (ext.toLowerCase() === 'pdf' && !validatePDF(buf)) errs.push('PDF corrupt')
  return errs
}

Type guard

const isSafeSvgName = (name: string): boolean =>
  !/\.svg$/i.test(name) || /^<\?xml/.test('') /* placeholder; use validateSvg on bytes */

Try / catch

try {
  await payload.create({ collection: 'media', file })
} catch (e) {
  if (e instanceof ValidationError) {
    // e.data.errors[0].message holds the joined reasons
    showUser(e.data.errors[0].message)
  } else throw e
}

Prevention

When it happens

Trigger: Uploading a file whose magic-byte-detected MIME disagrees with upload.mimeTypes (e.g. renamed .txt as .pdf); uploading an SVG containing <script> or external references; uploading a truncated/corrupt PDF; uploading an extension in the restricted list when mimeTypes is empty.

Common situations: Users renaming files to bypass extension filters; SVGs exported from design tools with inline scripts; PDFs truncated by interrupted uploads; allowing 'image/*' but receiving an SVG detected as application/xml without the SVG-XML detection branch firing; mimeTypes list missing the detected magic-byte type.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/95c8334539af20f7. Report an issue: GitHub.