evanw/esbuild · error · Error

Expected value for to be a string, got instead

Error message

Expected value for ${what}${key !== void 0 ? ' ' + quote(key) : ''} to be a string, got ${typeof value} instead

What it means

validateStringValue() at lib/shared/common.ts:140 guards every option value that becomes a `--flag:key=value` CLI token (define, logOverride, alias, banner, footer, loader, outExtension, external, inject, pure, drop, entryPoint values). Each such value must be a JS string; otherwise esbuild throws naming the context (`what`), the key, and the actual typeof it received.

Solutions

  1. Convert the value to a string (e.g. String(value), value.toString(), or regex .source).
  2. For define/replace values that must be string literals in output, wrap in quotes: `"'production'"`.
  3. Type the map as Record<string, string> in your own config so non-strings are caught upstream.

Example fix

// before
await build({ entryPoints: ['app.ts'], define: { NODE_ENV: true } })

// after
await build({ entryPoints: ['app.ts'], define: { NODE_ENV: 'true' } })
Defensive patterns

Strategy: type-guard

Validate before calling

function assertStringValues(map, name) {
  if (!map) return map
  for (const k of Object.keys(map)) {
    if (typeof map[k] !== 'string') {
      throw new Error(`${name}[${k}] must be a string, got ${typeof map[k]}`)
    }
  }
  return map
}
assertStringValues(options.define, 'define')
assertStringValues(options.loader, 'loader')

Type guard

function isStringRecord(v): v is Record<string, string> {
  if (!v || typeof v !== 'object') return false
  return Object.values(v).every(x => typeof x === 'string')
}
if (options.define && !isStringRecord(options.define)) {
  throw new Error('define values must all be strings')
}

Prevention

When it happens

Trigger: Passing `define: { NODE_ENV: true }`, `loader: { '.css': 1 }`, `external: [/^foo/]`, `pure: [function f(){}]`, or `entryPoints: { app: 42 }`.

Common situations: Using a native JS value (boolean, number, regex, function) instead of its string form; loading config from JSON with mixed types; forwarding untyped user input into these keyed maps.

Related errors


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

Appendix: source

Thrown at lib/shared/common.ts:142

type CommonOptions = types.BuildOptions | types.TransformOptions

function pushLogFlags(flags: string[], options: CommonOptions, keys: OptionKeys, isTTY: boolean, logLevelDefault: types.LogLevel): void {
  let color = getFlag(options, keys, 'color', mustBeBoolean)
  let logLevel = getFlag(options, keys, 'logLevel', mustBeString)
  let logLimit = getFlag(options, keys, 'logLimit', mustBeInteger)
  let logStyle = getFlag(options, keys, 'logStyle', mustBeString)

  if (color !== void 0) flags.push(`--color=${color}`)
  else if (isTTY) flags.push(`--color=true`); // This is needed to fix "execFileSync" which buffers stderr
  flags.push(`--log-level=${logLevel || logLevelDefault}`)
  flags.push(`--log-limit=${logLimit || 0}`)
  if (logStyle) flags.push(`--log-style=${logStyle}`)
}

function validateStringValue(value: unknown, what: string, key?: string): string {
  if (typeof value !== 'string') {
    throw new Error(`Expected value for ${what}${key !== void 0 ? ' ' + quote(key) : ''} to be a string, got ${typeof value} instead`)
  }
  return value
}

function pushCommonFlags(flags: string[], options: CommonOptions, keys: OptionKeys): void {
  let legalComments = getFlag(options, keys, 'legalComments', mustBeString)
  let sourceRoot = getFlag(options, keys, 'sourceRoot', mustBeString)
  let sourcesContent = getFlag(options, keys, 'sourcesContent', mustBeBoolean)
  let target = getFlag(options, keys, 'target', mustBeStringOrArrayOfStrings)
  let format = getFlag(options, keys, 'format', mustBeString)
  let globalName = getFlag(options, keys, 'globalName', mustBeString)
  let mangleProps = getFlag(options, keys, 'mangleProps', mustBeRegExp)
  let reserveProps = getFlag(options, keys, 'reserveProps', mustBeRegExp)
  let mangleQuoted = getFlag(options, keys, 'mangleQuoted', mustBeBoolean)
  let minify = getFlag(options, keys, 'minify', mustBeBoolean)
  let minifySyntax = getFlag(options, keys, 'minifySyntax', mustBeBoolean)
  let minifyWhitespace = getFlag(options, keys, 'minifyWhitespace', mustBeBoolean)
  let minifyIdentifiers = getFlag(options, keys, 'minifyIdentifiers', mustBeBoolean)

View on GitHub (pinned to f6058f8364)