payloadcms/payload · error · APIError
File exceeds the ${maxFileSize} byte upload limit.
Error message
File exceeds the ${maxFileSize} byte upload limit. What it means
Thrown as a 400 after a file is materialized (either via base64 decode or externalURL download) when its `file.size` exceeds the global `req.payload.config.upload.limits.fileSize` cap. This is the post-hoc size check — it runs on the final byte count, not on an estimate.
Source
Thrown at packages/plugin-mcp/src/mcp/builtin/collections/fileInput.ts:125
}
file = await getExternalFile({
data: {
filename: sanitizeFilename(input.name || getURLFilename(url)),
url: input.url,
} as FileData,
req,
uploadConfig: {
...uploadConfig,
externalFileHeaderFilter: uploadConfig.externalFileHeaderFilter ?? (() => ({})),
},
})
file.mimetype = file.mimetype?.split(';')[0] || 'application/octet-stream'
file.size = file.data.length
}
if (maxFileSize !== undefined && Number.isFinite(maxFileSize) && file.size > maxFileSize) {
throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400)
}
return file
}
function decodeBase64({ maxFileSize, value }: { maxFileSize?: number; value: string }): Buffer {
const normalized = value.replace(/\s/g, '')
if (!/^[a-z0-9+/]*={0,2}$/i.test(normalized) || normalized.length % 4 === 1) {
throw new APIError('File data must be valid base64.', 400)
}
if (maxFileSize !== undefined && Number.isFinite(maxFileSize)) {
const paddingLength = normalized.endsWith('==') ? 2 : normalized.endsWith('=') ? 1 : 0
const decodedSize = Math.floor((normalized.length * 3) / 4) - paddingLength
if (decodedSize > maxFileSize) {
throw new APIError(`File exceeds the ${maxFileSize} byte upload limit.`, 400)View on GitHub (pinned to 00c58b35c0)
Solutions
- Raise `config.upload.limits.fileSize` to the intended maximum (in bytes)
- Pre-validate file size before calling the tool — for `externalURL`, do a HEAD request and inspect `content-length`
- Compress or resize the asset so it fits under the configured limit
Example fix
// before
upload: { limits: { fileSize: 1_000_000 } } // 1 MB
// after
upload: { limits: { fileSize: 10_000_000 } } // 10 MB Defensive patterns
Strategy: validation
Validate before calling
// For externalURL: HEAD the resource and pre-check Content-Length
const head = await fetch(url, { method: 'HEAD' })
const len = Number(head.headers.get('content-length') ?? 0)
if (maxFileSize !== undefined && len > maxFileSize) throw new Error(`remote file too large: ${len}`) Try / catch
import { APIError } from 'payload'
try {
await tool.call({ source: 'externalURL', url })
} catch (e) {
if (e instanceof APIError && e.statusCode === 400 && /exceeds the .* byte upload limit/.test(e.message)) {
// tell the user the file is too large for the configured cap
}
throw e
} Prevention
- Set `config.upload.limits.fileSize` to the largest asset you genuinely intend to accept
- Do a HEAD request for externalURL sources to pre-validate size before downloading
- Show the configured size limit in the upload UI so users resize proactively
When it happens
Trigger: Downloading a remote file via `externalURL` that turns out larger than `config.upload.limits.fileSize`; a base64 payload that decoded to within the estimated limit (see error 368) but the actual `data.length` exceeds it; `maxFileSize` undefined/non-finite lets a huge file through to here only if the limit was added at a different layer.
Common situations: `config.upload.limits.fileSize` set lower than the assets users actually upload; remote URL that returns a larger body than its Content-Length header advertised; no client-side size check before the tool call.
Related errors
- Collection "${collectionSlug}" does not support file uploads
- Uploading files from URLs is disabled for collection "${coll
- Error initializing MCP handler: ${String(error)}
- Staged upload not found. Complete the upload action first, o
- File URLs must use http or https.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/1a6465a1f8e74a35.
Report an issue: GitHub.