payloadcms/payload · error · APIError
Staged upload not found. Complete the upload action first…
Error message
Staged upload not found. Complete the upload action first, or use base64 for small local files.
What it means
The MCP file-input resolver throws this 400 when `source: 'uploadReference'` is used but `getFileFromUploadInstructions` cannot locate a staged upload matching the supplied `file.uploadReference`. It is a deliberate re-wrap of the internal 'Staged upload was not found.' error so the caller knows the two-step upload flow was not completed.
Solutions
- Complete the dispatch upload (PUT the bytes to the signed URL returned by `generateUploadInstructions`) before calling the MCP tool with that `uploadReference`
- For small local files, switch to `source: 'base64'` which is single-step
- Regenerate upload instructions if the signed URL has expired and obtain a fresh `uploadReference`
- Verify the `uploadReference.prefix` matches the collection the tool is operating on
Example fix
// before — uploadReference supplied before the dispatch PUT
{ source: 'uploadReference', file: { filename, mimeType, size, uploadReference: { prefix } } }
// after — small file via base64, single step
{ source: 'base64', name: 'logo.png', mimeType: 'image/png', data: base64String } Defensive patterns
Strategy: validation
Validate before calling
// Verify the dispatch PUT succeeded before passing an uploadReference
async function ensureDispatched(signedUrl: string, bytes: Buffer) {
const res = await fetch(signedUrl, { method: 'PUT', body: bytes })
if (!res.ok) throw new Error(`dispatch upload failed: ${res.status}`)
} Try / catch
import { APIError } from 'payload'
try {
await tool.call({ source: 'uploadReference', file })
} catch (e) {
if (e instanceof APIError && e.statusCode === 400 && /Staged upload not found/.test(e.message)) {
// fall back to base64 for small files
return tool.call({ source: 'base64', name: file.filename, mimeType: file.mimeType, data: bytes.toString('base64') })
}
throw e
} Prevention
- Always run the dispatch PUT and confirm a 2xx before referencing the staged upload
- Treat uploadReference as single-use — do not replay it
- Default to base64 for files under a few hundred KB to avoid the two-step flow
- Regenerate upload instructions if more than ~3 hours pass (Azure SAS window)
When it happens
Trigger: Calling an MCP upload tool with `source: 'uploadReference'` before the dispatch PUT to the signed URL has finished; passing an `uploadReference` whose staged prefix was already consumed or whose SAS token expired; passing an uploadReference produced for collection A to a tool operating on collection B.
Common situations: Forgetting the dispatch step after `generateUploadInstructions`; calling the create/update MCP tool twice with the same reference (second call finds nothing); Azure SAS token older than 3 hours; cross-collection upload-reference reuse.
Related errors
- File data must be valid base64.
- Collection " " does not support file uploads.
- File exceeds the byte upload limit.
- File URLs must use http or https.
- MCP overrideAccess must be "true" or "false".
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/b3b6f815fae1ecfe.
Report an issue: GitHub.
Appendix: source
Thrown at packages/plugin-mcp/src/mcp/builtin/collections/fileInput.ts:61
export async function resolveFile({
collectionSlug,
input,
req,
}: {
collectionSlug: CollectionSlug
input?: FileInput
req: PayloadRequest
}): Promise<File | undefined> {
if (!input) {
return undefined
}
if (input.source === 'uploadReference') {
try {
return await getFileFromUploadInstructions({ collectionSlug, file: input.file, req })
} catch (error) {
if (error instanceof Error && error.message === 'Staged upload was not found.') {
throw new APIError(
'Staged upload not found. Complete the upload action first, or use base64 for small local files.',
400,
)
}
throw error
}
}
const uploadConfig = req.payload.collections[collectionSlug]?.config.upload
if (!uploadConfig) {
throw new APIError(`Collection "${collectionSlug}" does not support file uploads.`, 400)
}
const maxFileSize = req.payload.config.upload.limits?.fileSize
let file: File
if (input.source === 'base64') {View on GitHub (pinned to 00c58b35c0)