Budibase/budibase · error · BadRequestError

File "${fileName}" has an invalid extension: "${extension}"

Error message

File "${fileName}" has an invalid extension: "${extension}"

What it means

Thrown by uploadFile when the extension (lowercased) is in the InvalidFileExtensions blocklist from @budibase/shared-core. The check is enforced for public (unauthenticated) users and on non-self-hosted (cloud) deployments; self-hosted authenticated admins bypass it.

Source

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

      }

      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}"`
        )
      }

      if (ACTIVE_CONTENT_EXTENSIONS.has(extensionLower)) {
        throw new ActiveContentFileError(fileName)
      }

      const mimeType =
        typeof rawMimeType === "string" ? rawMimeType.toLowerCase() : undefined
      if (
        mimeType &&
        ACTIVE_CONTENT_MIME_TYPES.some(type => mimeType.includes(type))
      ) {
        throw new ActiveContentFileError(fileName)
      }

      if (

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Rename or repackage the file with an allowed extension (e.g. zip it first)
  2. Host the file externally and link to it instead of uploading
  3. For self-hosted environments where this is intended, ensure env.SELF_HOSTED=true and upload as an authenticated non-public user

Example fix

// before
upload 'installer.exe' as anonymous user in cloud
// after
zip to 'installer.zip' or host externally and link
Defensive patterns

Strategy: validation

Validate before calling

const BLOCKED = ['exe','bat','sh','cmd','com','scr','ps1'] // subset of InvalidFileExtensions
function isExtensionAllowed(name) {
  const ext = (name.split('.').pop() || '').toLowerCase()
  return !BLOCKED.includes(ext)
}

Type guard

const isSafeExtension = (name: string): boolean =>
  !InvalidFileExtensions.includes((name.split('.').pop() || '').toLowerCase())

Try / catch

try {
  await api.uploadFile(fd)
} catch (err) {
  if (err instanceof BadRequestError && err.message.includes('invalid extension')) {
    // package the file (e.g. zip) or host externally
  } else throw err
}

Prevention

When it happens

Trigger: Uploading a file with a blacklisted extension (e.g. .exe, .bat, .sh) when the requester's roleId is PUBLIC or env.SELF_HOSTED is false.

Common situations: Distributing installers/scripts via Budibase attachments in cloud apps, or self-hosters confused because the same file works locally but is blocked in cloud.

Related errors


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