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
- Verify the fileKey matches what upload returned and that the file exists under the configured local upload directory.
- If running local-file in a container, mount a persistent volume for the upload dir so files survive restarts.
- 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
- Mount a persistent volume for the local upload dir in containers.
- Clean up DB records when deleting local files.
- Re-upload or migrate assets when switching file providers.
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
- --config file must be of type .json or .yaml - ${configFileC
- invalid_data
- Key ${key} already exists in app metadata
- invalid_data
- invalid_data
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/c14c721260ca8867.
Report an issue: GitHub.