Budibase/budibase · error · BadRequestError

File " " has no extension, an extension is required to…

Error message

File "${fileName}" has no extension, an extension is required to upload a file

What it means

Thrown by uploadFile when the filename has no extension (the last dot-separated segment is empty). Extensions drive storage keys, content type resolution, and the security extension checks, so extensionless uploads are rejected.

Solutions

  1. Rename the file to include an extension before upload (e.g. notes.txt)
  2. Append a filename with extension when creating the FormData entry
  3. Pick an extension matching the actual content type

Example fix

// before
fd.append('file', blob, 'config')
// after
fd.append('file', blob, 'config.json')
Defensive patterns

Strategy: validation

Validate before calling

function hasExtension(name) {
  const ext = name.split('.').pop()
  return Boolean(ext) && name.includes('.')
}

Type guard

const hasFileExtension = (f: { name: string }): f is { name: string } & { ext: string } =>
  f.name.includes('.') && f.name.split('.').pop()!.length > 0

Try / catch

try {
  await api.uploadFile(fd)
} catch (err) {
  if (err instanceof BadRequestError && err.message.includes('has no extension')) {
    // rename to include an extension and resend
  } else throw err
}

Prevention

When it happens

Trigger: Uploading files named like 'README' or '.gitignore' (split('.') yields empty last segment) or blobs given extensionless names.

Common situations: macOS/Linux config or dotfiles, scripts uploading temp files without names, or API clients hardcoding names without extensions.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/df7f6547b6a32e6c. Report an issue: GitHub.

Appendix: source

Thrown at packages/server/src/api/controllers/static/index.ts:274

  let files = file && Array.isArray(file) ? Array.from(file) : [file]

  ctx.body = await Promise.all(
    files.map(async file => {
      const fileName = getUploadFilename(file)
      const filePath = getUploadPath(file)
      const rawMimeType = getUploadMimeType(file)
      if (!fileName) {
        throw new BadRequestError(
          "Attempted to upload a file without a filename"
        )
      }
      if (!filePath) {
        throw new BadRequestError("Attempted to upload a file without a path")
      }

      const extension = [...fileName.split(".")].pop()
      if (!extension) {
        throw new BadRequestError(
          `File "${fileName}" has no extension, an extension is required to upload a file`
        )
      }

      const extensionLower = extension.toLowerCase()
      const isPublicUser =
        ctx.roleId === roles.BUILTIN_ROLE_IDS.PUBLIC ||
        ctx.user?.roleId === roles.BUILTIN_ROLE_IDS.PUBLIC
      const enforceInvalidExtension = isPublicUser || !env.SELF_HOSTED
      if (
        enforceInvalidExtension &&
        InvalidFileExtensions.includes(extensionLower)
      ) {
        throw new BadRequestError(
          `File "${fileName}" has an invalid extension: "${extension}"`
        )
      }

View on GitHub (pinned to a81a902e9a)