medusajs/medusa · error · MedusaError

No file provided

Error message

No file provided

What it means

The local file service's upload rejects a falsy file argument with INVALID_DATA before doing anything else — there is no file object to read a filename or stream from.

Source

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

  constructor(_, options: LocalFileServiceOptions) {
    super()
    this.uploadDir_ = options?.upload_dir || path.join(process.cwd(), "static")

    // Since there is no way to serve private files through a static server, we simply place them in `static`.
    // This means that the files will be available publicly if the filename is known. Since the local file provider
    // is for development only, this shouldn't be an issue. If you really want to use it in production (and you shouldn't)
    // You can change the private upload dir to `/private` but none of the functionalities where you use a presigned URL will work.
    this.privateUploadDir_ =
      options?.private_upload_dir || path.join(process.cwd(), "static")
    this.backendUrl_ = options?.backend_url || "http://localhost:9000/static"
  }

  async upload(
    file: FileTypes.ProviderUploadFileDTO
  ): Promise<FileTypes.ProviderFileResultDTO> {
    if (!file) {
      throw new MedusaError(MedusaError.Types.INVALID_DATA, `No file provided`)
    }

    if (!file.filename) {
      throw new MedusaError(
        MedusaError.Types.INVALID_DATA,
        `No filename provided`
      )
    }

    const parsedFilename = path.parse(file.filename)
    const baseDir =
      file.access === "public" ? this.uploadDir_ : this.privateUploadDir_
    await this.ensureDirExists(baseDir, parsedFilename.dir)

    const fileKey = path.join(
      parsedFilename.dir,
      // We prepend "private" to the file key so deletions and presigned URLs can know which folder to look into
      `${file.access === "public" ? "" : "private-"}${Date.now()}-${

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Check the multipart/form-data parse result before delegating: if no file part exists, return a 400 instead of calling upload.
  2. On the client, ensure the file field name matches what the server/middleware expects and that Content-Type is multipart/form-data.
  3. In route code, validate file presence before invoking the file service.

Example fix

// before
const result = await fileService.upload(req.body.file) // undefined when no file part
// after
if (!req.file) return res.status(400).json({ message: "No file provided" })
const result = await fileService.upload(req.file)
Defensive patterns

Strategy: validation

Validate before calling

if (!file) {
  return res.status(400).json({ message: "No file provided" })
}
await fileService.upload(file)

Type guard

const isUploadFileDTO = (f: unknown): f is FileTypes.ProviderUploadFileDTO =>
  !!f && typeof f === "object" && typeof (f as any).filename === "string"

Try / catch

try { await fileService.upload(file) } catch (e) { if (e instanceof MedusaError && /No file provided/.test(e.message)) { res.status(400).json({ message: e.message }); return } throw e }

Prevention

When it happens

Trigger: Calling provider.upload(undefined/null), typically because an upstream form-parse failed or an empty multipart body was submitted.

Common situations: An upload API route forwarding req.body when the client sent no file; tests calling the service directly with no argument; a refactor that renamed the variable holding the parsed file.

Related errors


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