payloadcms/payload · error · FileRetrievalError

There was a problem while uploading the file. ${message}

Error message

There was a problem while uploading the file. ${message}

What it means

When duplicating or re-uploading, Payload tries to re-acquire the original file either locally (`getFileByPath`) or remotely (`getExternalFile`). Any error thrown inside that block — disk read failure, missing local file, DNS error, 4xx/5xx on the remote fetch, SSRF block — is wrapped and re-thrown as a `FileRetrievalError` (HTTP 500). The original error message is appended (`There was a problem while retrieving the file. <msg>`) so callers can see the root cause.

Source

Thrown at packages/payload/src/uploads/generateFileData.ts:146

    try {
      if (!disableLocalStorage && isLocalFile) {
        // File is stored locally
        const filePath = `${staticPath}/${filename}`
        const response = await getFileByPath(filePath)
        file = response
        overwriteExistingFiles = true
      } else if (filename && url) {
        // File is remote
        file = await getExternalFile({
          data: incomingFileData as unknown as FileData,
          req,
          uploadConfig: collectionConfig.upload,
        })
        overwriteExistingFiles = true
      }
    } catch (err: unknown) {
      throw new FileRetrievalError(req.t, err instanceof Error ? err.message : undefined)
    }
  }

  if (isDuplicating) {
    overwriteExistingFiles = false
  }

  if (!file) {
    if (throwOnMissingFile) {
      throw new MissingFile(req.t)
    }

    return {
      data: incomingFileData!,
      files: [],
    }
  }

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Check server logs for the appended root-cause message to identify local-vs-remote failure.
  2. For local failures: confirm the file exists at `${staticPath}/${filename}` and the process has read permission; restore from backup or re-upload.
  3. For remote failures: verify the `url` is reachable and the server can egress to it (no firewall/proxy block); refresh expired signed URLs.
  4. Run a storage-consistency sweep to find dangling references and either re-upload or delete the orphan documents.
  5. If migrating storage, backfill `staticDir`/`staticURL` so existing documents resolve correctly.

Example fix

// before — duplicate references a missing local file
await payload.create({ collection: 'media', data: { _duplicateFrom: id } })

// after — verify the source exists first, else re-upload
import fs from 'node:fs/promises'
try {
  await fs.access(`${staticDir}/${doc.filename}`)
  await payload.create({ collection: 'media', data: { _duplicateFrom: id } })
} catch {
  // source gone — fetch bytes and create with req.file instead
}
Defensive patterns

Strategy: try-catch

Validate before calling

import fs from 'node:fs/promises'

async function sourceReadable(staticDir: string, filename: string | null, url?: string | null) {
  if (filename && (filename.includes('../') || filename.includes('..\\'))) return false
  if (!filename) return false
  try {
    await fs.access(`${staticDir}/${filename}`)
    return true
  } catch {
    return false
  }
}

if (!(await sourceReadable(staticDir, doc.filename, doc.url))) {
  // re-upload bytes instead of duplicating from a missing source
}

Type guard

import { APIError } from 'payload'
function isFileRetrievalError(err: unknown): err is InstanceType<typeof APIError> {
  return err instanceof Error && /problem while retrieving the file/i.test(err.message)
}

Try / catch

try {
  await payload.create({ collection: 'media', data: { _duplicateFrom: id } })
} catch (err) {
  if (isFileRetrievalError(err)) {
    // log the appended root cause, then re-upload the bytes directly
  } else throw err
}

Prevention

When it happens

Trigger: A duplicate/update with `isDuplicating` or `shouldReupload` true, where: (a) local storage is enabled and `getFileByPath(`${staticPath}/${filename}`)` fails (ENOENT, permission denied), or (b) the file is remote and `getExternalFile` throws (network, redirect limit, SSRF, non-ok status). The surrounding `catch (err)` converts any such error into `FileRetrievalError`.

Common situations: The media directory was moved/rotated between deploys and the stored path no longer exists. Local storage was disabled (`disableLocalStorage`) but the document still references a local URL. The remote URL expired (signed S3 link, CDN purge). A storage migration left dangling references. Filesystem permissions changed.

Related errors


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