medusajs/medusa · error · MedusaError

File with key ${file.fileKey} not found

Error message

File with key ${file.fileKey} not found

What it means

Before minting a (local) presigned download URL, the service checks the file exists on disk with fs.access(F_OK); if not found it throws NOT_FOUND naming the fileKey, because serving a URL for a nonexistent local file would just 404 later.

Source

Thrown at packages/modules/providers/file-local/src/services/local-file.ts:210

      : this.uploadDir_

    const filePath = this.getUploadFilePath(baseDir, file.fileKey)
    return fs.readFile(filePath)
  }

  // The local file provider doesn't support presigned URLs for private files (i.e files not placed in /static).
  async getPresignedDownloadUrl(
    file: FileTypes.ProviderGetFileDTO
  ): Promise<string> {
    const isPrivate = file.fileKey.startsWith("private-")
    const baseDir = isPrivate ? this.privateUploadDir_ : this.uploadDir_

    const filePath = this.getUploadFilePath(baseDir, file.fileKey)

    try {
      await fs.access(filePath, fs.constants.F_OK)
    } catch {
      throw new MedusaError(
        MedusaError.Types.NOT_FOUND,
        `File with key ${file.fileKey} not found`
      )
    }

    return this.getUploadFileUrl(file.fileKey)
  }

  /**
   * Returns the pre-signed URL that the client (frontend) can use to trigger
   * a file upload. In this case, the Medusa backend will implement the
   * "/upload" endpoint to perform the file upload.
   */
  async getPresignedUploadUrl(
    fileData: FileTypes.ProviderGetPresignedUploadUrlDTO
  ): Promise<FileTypes.ProviderFileResultDTO> {
    if (!fileData?.filename) {
      throw new MedusaError(

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Verify the fileKey matches what upload returned and that the file exists under the configured local upload directory.
  2. If running local-file in a container, mount a persistent volume for the upload dir so files survive restarts.
  3. Delete stale DB references or re-upload the missing assets.

Example fix

// before
const { url } = await fileService.getPresignedDownloadUrl({ fileKey: row.file_key })
// after
const { url } = await fileService.getPresignedDownloadUrl({ fileKey: row.file_key, isPrivate: row.private }).catch((e) => {
  if (e.type === MedusaError.Types.NOT_FOUND) return null // treat as missing asset
  throw e
})
Defensive patterns

Strategy: fallback

Validate before calling

import fs from "fs/promises"
async function fileExists(path: string): Promise<boolean> {
  try { await fs.access(path); return true } catch { return false }
}

Type guard

const isMissingFileError = (e: unknown): boolean =>
  e instanceof MedusaError && e.type === MedusaError.Types.NOT_FOUND && /not found/.test(e.message)

Try / catch

try {
  const { url } = await fileService.getPresignedDownloadUrl({ fileKey })
} catch (e) {
  if (isMissingFileError(e)) {
    // fallback: placeholder asset or 404 page
    return res.status(404).json({ message: "File no longer available" })
  }
  throw e
}

Prevention

When it happens

Trigger: Calling getFileDownloadUrl/getPresignedDownloadUrl with a fileKey that was never uploaded, was deleted from the local storage directory, or points outside the provider's base directory naming scheme (e.g. missing the prefix folder).

Common situations: DB rows referencing files uploaded under a different upload_dir or a previous container (local storage is ephemeral in docker — files vanish on redeploy); fileKey typo or missing prefix; the file was soft-deleted but the record retained; switching file providers (S3→local) without migrating objects.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/c14c721260ca8867. Report an issue: GitHub.