{"id":"86f2856c303982bc","repo":"evanw/esbuild","slug":"invalid-loader","errorCode":null,"errorMessage":"Invalid loader","messagePattern":"Invalid loader","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/api/api_impl.go","lineNumber":286,"sourceCode":"\t\treturn config.LoaderGlobalCSS\n\tcase LoaderJS:\n\t\treturn config.LoaderJS\n\tcase LoaderJSON:\n\t\treturn config.LoaderJSON\n\tcase LoaderJSX:\n\t\treturn config.LoaderJSX\n\tcase LoaderLocalCSS:\n\t\treturn config.LoaderLocalCSS\n\tcase LoaderNone:\n\t\treturn config.LoaderNone\n\tcase LoaderText:\n\t\treturn config.LoaderText\n\tcase LoaderTS:\n\t\treturn config.LoaderTS\n\tcase LoaderTSX:\n\t\treturn config.LoaderTSX\n\tdefault:\n\t\tpanic(\"Invalid loader\")\n\t}\n}\n\nfunc extractPathStyle(absPaths AbsPaths, flag AbsPaths) logger.PathStyle {\n\tif (absPaths & flag) != 0 {\n\t\treturn logger.AbsPath\n\t} else {\n\t\treturn logger.RelPath\n\t}\n}\n\nvar versionRegex = regexp.MustCompile(`^([0-9]+)(?:\\.([0-9]+))?(?:\\.([0-9]+))?(-[A-Za-z0-9]+(?:\\.[A-Za-z0-9]+)*)?$`)\n\nfunc validateFeatures(log logger.Log, target Target, engines []Engine) (compat.JSFeature, compat.CSSFeature, map[css_ast.D]compat.CSSPrefix, string) {\n\tif target == DefaultTarget && len(engines) == 0 {\n\t\treturn 0, 0, nil, \"\"\n\t}\n","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/pkg/api/api_impl.go#L268-L304","documentation":"A panic raised by validateLoader when the Loader value is not one of the documented loader constants (LoaderBase64, LoaderBinary, LoaderCopy, LoaderCSS, LoaderDataURL, LoaderDefault, LoaderEmpty, LoaderFile, LoaderGlobalCSS, LoaderJS, LoaderJSON, LoaderJSX, LoaderLocalCSS, LoaderNone, LoaderText, LoaderTS, LoaderTSX). The JS API maps loader strings to these constants and cannot reach the panic; only Go-API misuse (out-of-range Loader) can.","triggerScenarios":"Go API: BuildOptions.Loader['.x'] = Loader(99). The JS API's Loader union is closed and validated before reaching validateLoader. Also fires when a loader value is computed via reflection without bounds checking.","commonSituations":"Reflective config decoding; fork that adds/removes a loader without updating the switch; unsafe casts; mapping a file extension to a loader via a numeric lookup table.","solutions":["Use only the documented Loader constants.","Validate deserialized loader values against an allowlist of constants.","Do not cast integers to Loader.","Pin a single esbuild version."],"exampleFix":"// before\nopts.Loader = map[string]api.Loader{ \".x\": api.Loader(99) }\n\n// after\nopts.Loader = map[string]api.Loader{ \".x\": api.LoaderText }","handlingStrategy":"type-guard","validationCode":"func validLoader(l api.Loader) bool {\n  switch l {\n  case api.LoaderNone, api.LoaderBase64, api.LoaderBinary, api.LoaderCopy, api.LoaderCSS, api.LoaderDataURL, api.LoaderDefault, api.LoaderEmpty, api.LoaderFile, api.LoaderGlobalCSS, api.LoaderJS, api.LoaderJSON, api.LoaderJSX, api.LoaderLocalCSS, api.LoaderText, api.LoaderTS, api.LoaderTSX:\n    return true\n  }\n  return false\n}","typeGuard":"type Loader = 'base64' | 'binary' | 'copy' | 'css' | 'data-url' | 'default' | 'empty' | 'file' | 'global-css' | 'js' | 'json' | 'jsx' | 'local-css' | 'text' | 'ts' | 'tsx'\nconst LOADERS = new Set<Loader>(['base64','binary','copy','css','data-url','default','empty','file','global-css','js','json','jsx','local-css','text','ts','tsx'])\nfunction isLoader(v: unknown): v is Loader {\n  return typeof v === 'string' && (LOADERS as Set<string>).has(v)\n}","tryCatchPattern":null,"preventionTips":["Use only documented Loader constants.","Validate per-extension loader maps built from user config.","Do not cast integers to Loader."],"tags":["api","loader","panic","invariant","go-api"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}