payloadcms/payload · error · APIError
File data must be valid base64.
Error message
File data must be valid base64.
What it means
Thrown as a 400 inside `decodeBase64` when the input fails the format guard — the regex `^[a-z0-9+/]*={0,2}$` (case-insensitive) does not match, or the length modulo 4 is 1 (an impossible base64 length). This catches obvious non-base64 strings before any decoding attempt.
Source
Thrown at packages/plugin-mcp/src/mcp/builtin/collections/fileInput.ts:135
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)
}
}
const data = Buffer.from(normalized, 'base64')
if (data.toString('base64').replace(/=+$/, '') !== normalized.replace(/=+$/, '')) {
throw new APIError('File data must be valid base64.', 400)
}
return dataView on GitHub (pinned to 00c58b35c0)
Solutions
- Strip any `data:...;base64,` prefix before passing the value
- Use standard base64 encoding (A–Z, a–z, 0–9, +, /) with 0–2 `=` padding characters
- For URL-safe base64, convert `-`→`+` and `_`→`/` and add padding before sending
Example fix
// before
data: 'data:image/png;base64,iVBORw0KGgo='
// after
const raw = 'iVBORw0KGgo='
new FileInput({ source: 'base64', name: 'x.png', mimeType: 'image/png', data: raw }) Defensive patterns
Strategy: validation
Validate before calling
// Reject obviously non-base64 input before calling the tool
function assertBase64(v: string) {
const norm = v.replace(/\s/g, '')
if (!/^[a-z0-9+/]*={0,2}$/i.test(norm) || norm.length % 4 === 1)
throw new Error('value is not valid base64 — strip data: prefix and use standard alphabet')
} Type guard
function looksLikeBase64(v: string): boolean {
const n = v.replace(/\s/g, '')
return /^[a-z0-9+/]*={0,2}$/i.test(n) && n.length % 4 !== 1
} Try / catch
import { APIError } from 'payload'
try {
await tool.call({ source: 'base64', data })
} catch (e) {
if (e instanceof APIError && e.statusCode === 400 && /must be valid base64/.test(e.message)) {
// re-encode the source bytes canonically and retry
}
throw e
} Prevention
- Always strip `data:<mime>;base64,` prefixes before sending
- Use standard base64 (not URL-safe) and add correct `=` padding
- Generate base64 with a trusted encoder rather than hand-editing
When it happens
Trigger: Passing a data-URL-prefixed string like `data:image/png;base64,...` as `source: 'base64'` `data`; passing raw binary or UTF-8 text; passing hex-encoded bytes; a string whose length % 4 === 1.
Common situations: Client reusing an `<img src="data:...">` value verbatim; forgetting to strip the base64 prefix; encoding with a non-standard alphabet (URL-safe base64 with `-`/`_`); truncation that leaves an invalid length.
Related errors
- Staged upload not found. Complete the upload action first, o
- No file data provided for import
- MCP overrideAccess must be "true" or "false".
- Collection "${collectionSlug}" does not support file uploads
- Uploading files from URLs is disabled for collection "${coll
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/1e70f9caf498643e.
Report an issue: GitHub.