payloadcms/payload · error · APIError

Invalid staged upload.

Error message

Invalid staged upload.

What it means

Thrown by `getStagedFile` when the `uploadReference` object passed to a document create/update is structurally invalid -- it must be a non-null, non-array object containing a string `uploadId`. This reference is what links a document mutation to the bytes previously staged via the signed upload URL.

Source

Thrown at packages/payload/src/uploads/stagedUpload.ts:161

 * it, and the temporary file is deleted after it is read.
 */
export const getStagedFile = async ({
  collectionSlug,
  req,
  uploadReference,
}: {
  collectionSlug: string
  req: PayloadRequest
  uploadReference: unknown
}): Promise<File> => {
  if (
    !uploadReference ||
    typeof uploadReference !== 'object' ||
    Array.isArray(uploadReference) ||
    !('uploadId' in uploadReference) ||
    typeof uploadReference.uploadId !== 'string'
  ) {
    throw new APIError('Invalid staged upload.', 400)
  }

  const { uploadId } = uploadReference
  const upload = await verifyUploadID(req, uploadId)

  if (upload.collectionSlug !== collectionSlug || upload.user !== getUser(req)) {
    throw new Forbidden(req.t)
  }

  const directory = await getUploadDirectory(req, upload.collectionSlug)
  const tempFilePath = path.join(directory, upload.id)
  let data: Buffer

  try {
    data = await fs.readFile(tempFilePath)
  } catch {
    throw new APIError('Staged upload was not found.', 400)
  }

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Pass the `uploadReference` object exactly as returned by `generateStagedUploadInstructions` -- do not reconstruct it.
  2. Ensure the value is JSON-serializable and survives any FormData / nested-field encoding (check that nested objects are not stringified twice or flattened).
  3. Validate the shape client-side before the request (see validationCode).
  4. If migrating from a legacy upload flow, update the client to use the staged-upload `uploadReference` shape.

Example fix

// before
await payload.create({
  collection: 'media',
  data: { file: { uploadReference: myUploadId } }, // uploadId at wrong level
})

// after
const instructions = await payloadUploadApi.getInstructions(...)
await payload.create({
  collection: 'media',
  data: {
    file: { uploadReference: { uploadId: instructions.file.uploadReference.uploadId } },
  },
})
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the uploadReference shape before sending
function isValidUploadReference(v) {
  return !!v && typeof v === 'object' && !Array.isArray(v)
    && 'uploadId' in v && typeof v.uploadId === 'string'
}
if (!isValidUploadReference(file.uploadReference)) {
  throw new Error('uploadReference must be { uploadId: string }')
}

Type guard

const isUploadReference = (v) => !!v && typeof v === 'object' && !Array.isArray(v) && 'uploadId' in v && typeof v.uploadId === 'string'

Try / catch

try {
  await payload.create({ collection, data })
} catch (e) {
  if (e instanceof APIError && e.message === 'Invalid staged upload.') {
    // regenerate instructions and retry
  } else throw e
}

Prevention

When it happens

Trigger: A create/update request sends `file: { uploadReference: ... }` where `uploadReference` is undefined, a primitive, an array, missing the `uploadId` key, or has a non-string `uploadId`.

Common situations: Client code constructs the `uploadReference` manually instead of echoing back the object from `generateStagedUploadInstructions`; a serialization layer (e.g. FormData) dropped the nested object; the client is using an older API shape that passed a plain filename string.

Related errors


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