{"record":{"id":"bb54a165d0afa507","repo":"evanw/esbuild","slug":"invalid-loader-extension-ext","errorCode":null,"errorMessage":"Invalid loader extension: ${ext}","messagePattern":"Invalid loader extension: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":335,"sourceCode":"      flags.push(`--alias:${old}=${validateStringValue(alias[old], 'alias', old)}`)\n    }\n  }\n  if (banner) {\n    for (let type in banner) {\n      if (type.indexOf('=') >= 0) throw new Error(`Invalid banner file type: ${type}`)\n      flags.push(`--banner:${type}=${validateStringValue(banner[type], 'banner', type)}`)\n    }\n  }\n  if (footer) {\n    for (let type in footer) {\n      if (type.indexOf('=') >= 0) throw new Error(`Invalid footer file type: ${type}`)\n      flags.push(`--footer:${type}=${validateStringValue(footer[type], 'footer', type)}`)\n    }\n  }\n  if (inject) for (let path of inject) flags.push(`--inject:${validateStringValue(path, 'inject')}`)\n  if (loader) {\n    for (let ext in loader) {\n      if (ext.indexOf('=') >= 0) throw new Error(`Invalid loader extension: ${ext}`)\n      flags.push(`--loader:${ext}=${validateStringValue(loader[ext], 'loader', ext)}`)\n    }\n  }\n  if (outExtension) {\n    for (let ext in outExtension) {\n      if (ext.indexOf('=') >= 0) throw new Error(`Invalid out extension: ${ext}`)\n      flags.push(`--out-extension:${ext}=${validateStringValue(outExtension[ext], 'out extension', ext)}`)\n    }\n  }\n\n  if (entryPoints) {\n    if (Array.isArray(entryPoints)) {\n      for (let i = 0, n = entryPoints.length; i < n; i++) {\n        let entryPoint = entryPoints[i]\n        if (typeof entryPoint === 'object' && entryPoint !== null) {\n          let entryPointKeys: OptionKeys = Object.create(null)\n          let input = getFlag(entryPoint, entryPointKeys, 'in', mustBeString)\n          let output = getFlag(entryPoint, entryPointKeys, 'out', mustBeString)","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/evanw/esbuild/blob/f6058f8364fe7ab91ca57a83e02577ed74c9cae4/lib/shared/common.ts#L317-L353","documentation":"The `loader` option is a map of file extension to loader name, serialized as `--loader:.EXT=NAME`. An '=' inside the extension breaks parsing, so esbuild rejects it at lib/shared/common.ts:335. Keys should be extensions like '.js', '.ts', '.png'.","triggerScenarios":"Passing `loader: { '.js=x': 'jsx' }` or any loader key containing '='.","commonSituations":"Generated extension strings that embed '='; using a full filename or glob instead of a bare extension.","solutions":["Use a plain extension key without '=' (e.g. '.json', '.png').","If you meant to set a loader by filename, use the per-entry-point loader via stdin/entry options or a plugin instead."],"exampleFix":"// before\nawait build({ entryPoints: ['app.ts'], loader: { '.txt=data': 'text' } })\n\n// after\nawait build({ entryPoints: ['app.ts'], loader: { '.txt': 'text' } })","handlingStrategy":"validation","validationCode":"function assertLoaderKeys(map) {\n  for (const k of Object.keys(map ?? {})) {\n    if (!k.startsWith('.') || k.includes('=')) {\n      throw new Error(`loader key must be an extension like '.ts', got ${k}`)\n    }\n  }\n}\nassertLoaderKeys(options.loader)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use bare extensions ('.ts', '.png') as loader keys, not filenames or globs.","Don't embed '=' in extension keys.","For per-file loaders, use the stdin/entry-point loader option or a plugin."],"tags":["config","validation","loader"],"backgroundTag":null,"analyzedSha":"f6058f8364fe7ab91ca57a83e02577ed74c9cae4","analyzedAt":"2026-08-09T18:37:22.223Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}