Budibase/budibase · error · BadRequestError

File "${fileName}" has no extension, an extension is require

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.

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)

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.