evanw/esbuild · error · Error

Invalid out extension

Error message

Invalid out extension: ${ext}

What it means

The `outExtension` option is a map of output type to extension, serialized as `--out-extension:TYPE=EXT`. An '=' inside the TYPE breaks parsing, so esbuild rejects it at lib/shared/common.ts:341. Valid types are '.js' and '.css'.

Solutions

  1. Use a valid output type key without '=' (either '.js' or '.css').
  2. Strip '=' from generated keys.

Example fix

// before
await build({ entryPoints: ['app.ts'], outExtension: { '.js=x': '.mjs' } })

// after
await build({ entryPoints: ['app.ts'], outExtension: { '.js': '.mjs' } })
Defensive patterns

Strategy: validation

Validate before calling

const VALID_OUTEXT_TYPES = new Set(['.js', '.css'])
function assertOutExtKeys(map) {
  for (const k of Object.keys(map ?? {})) {
    if (!VALID_OUTEXT_TYPES.has(k)) throw new Error(`outExtension key must be '.js' or '.css', got ${k}`)
  }
}
assertOutExtKeys(options.outExtension)

Prevention

When it happens

Trigger: Passing `outExtension: { '.js=x': '.mjs' }` or any outExtension key containing '='.

Common situations: Typo or generated key; confusion between the type key ('.js'/'.css') and the value.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/40fdcfa32bd57559. Report an issue: GitHub.

Appendix: source

Thrown at lib/shared/common.ts:341

      flags.push(`--banner:${type}=${validateStringValue(banner[type], 'banner', type)}`)
    }
  }
  if (footer) {
    for (let type in footer) {
      if (type.indexOf('=') >= 0) throw new Error(`Invalid footer file type: ${type}`)
      flags.push(`--footer:${type}=${validateStringValue(footer[type], 'footer', type)}`)
    }
  }
  if (inject) for (let path of inject) flags.push(`--inject:${validateStringValue(path, 'inject')}`)
  if (loader) {
    for (let ext in loader) {
      if (ext.indexOf('=') >= 0) throw new Error(`Invalid loader extension: ${ext}`)
      flags.push(`--loader:${ext}=${validateStringValue(loader[ext], 'loader', ext)}`)
    }
  }
  if (outExtension) {
    for (let ext in outExtension) {
      if (ext.indexOf('=') >= 0) throw new Error(`Invalid out extension: ${ext}`)
      flags.push(`--out-extension:${ext}=${validateStringValue(outExtension[ext], 'out extension', ext)}`)
    }
  }

  if (entryPoints) {
    if (Array.isArray(entryPoints)) {
      for (let i = 0, n = entryPoints.length; i < n; i++) {
        let entryPoint = entryPoints[i]
        if (typeof entryPoint === 'object' && entryPoint !== null) {
          let entryPointKeys: OptionKeys = Object.create(null)
          let input = getFlag(entryPoint, entryPointKeys, 'in', mustBeString)
          let output = getFlag(entryPoint, entryPointKeys, 'out', mustBeString)
          checkForInvalidFlags(entryPoint, entryPointKeys, 'in entry point at index ' + i)
          if (input === undefined) throw new Error('Missing property "in" for entry point at index ' + i)
          if (output === undefined) throw new Error('Missing property "out" for entry point at index ' + i)
          entries.push([output, input])
        } else {
          entries.push(['', validateStringValue(entryPoint, 'entry point at index ' + i)])

View on GitHub (pinned to f6058f8364)