evanw/esbuild · error · Error

Expected in mangle cache to map to either a string or false

Error message

Expected ${quote(key)} in mangle cache to map to either a string or false

What it means

The `mangleCache` option maps property names to either their mangled replacement (a string) or to `false` (meaning 'do not mangle this property'). validateMangleCache() at lib/shared/common.ts:109 rejects any other value type (number, null, true, array, object). This keeps the cache round-trippable between builds.

Solutions

  1. Ensure every value in the mangleCache object is a string or exactly the boolean `false`.
  2. Feed back the mangleCache exactly as esbuild returned it in the previous result.
  3. If you must transform values, coerce to string or `false` explicitly before passing.

Example fix

// before
await build({ entryPoints: ['app.ts'], mangleProps: /^_/, mangleCache: { _secret: 0 } })

// after
await build({ entryPoints: ['app.ts'], mangleProps: /^_/, mangleCache: { _secret: false } })
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeMangleCache(cache) {
  if (!cache) return cache
  const out = Object.create(null)
  for (const key of Object.keys(cache)) {
    const v = cache[key]
    if (typeof v !== 'string' && v !== false) {
      throw new Error(`mangleCache[${key}] must be string or false, got ${typeof v}`)
    }
    out[key] = v
  }
  return out
}
options.mangleCache = sanitizeMangleCache(options.mangleCache)

Type guard

function isMangleCache(v): v is Record<string, string | false> {
  if (!v || typeof v !== 'object') return false
  return Object.values(v).every(x => typeof x === 'string' || x === false)
}
if (options.mangleCache && !isMangleCache(options.mangleCache)) {
  throw new Error('invalid mangleCache')
}

Prevention

When it happens

Trigger: Passing `mangleCache: { prop: 0 }`, `{ prop: null }`, `{ prop: true }`, or `{ prop: { x: 1 } }` to build()/context()/transform().

Common situations: Hand-editing or programmatically transforming a mangleCache returned by a previous build; serializing/deserializing the cache through a layer that coerces booleans; merging caches from multiple sources with non-conforming values.

Related errors


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

Appendix: source

Thrown at lib/shared/common.ts:118

  return {
    wasmURL,
    wasmModule,
    worker,
  }
}

type MangleCache = Record<string, string | false>

function validateMangleCache(mangleCache: MangleCache | undefined): MangleCache | undefined {
  let validated: MangleCache | undefined
  if (mangleCache !== undefined) {
    validated = Object.create(null) as MangleCache
    for (let key in mangleCache) {
      let value = mangleCache[key]
      if (typeof value === 'string' || value === false) {
        validated[key] = value
      } else {
        throw new Error(`Expected ${quote(key)} in mangle cache to map to either a string or false`)
      }
    }
  }
  return validated
}

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

View on GitHub (pinned to f6058f8364)