{"id":"d9c3d274a01858bc","repo":"evanw/esbuild","slug":"expected-onload-callback-in-plugin-quote-name","errorCode":null,"errorMessage":"Expected onLoad() callback in plugin ${quote(name)} to return an object","messagePattern":"Expected onLoad\\(\\) callback in plugin (.+?) to return an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1446,"sourceCode":"    }\n    sendResponse(id, response as any)\n  }\n\n  requestCallbacks['on-load'] = async (id, request: protocol.OnLoadRequest) => {\n    let response: protocol.OnLoadResponse = {}, name = '', callback, note\n    for (let id of request.ids) {\n      try {\n        ({ name, callback, note } = onLoadCallbacks[id])\n        let result = await callback({\n          path: request.path,\n          namespace: request.namespace,\n          suffix: request.suffix,\n          pluginData: details.load(request.pluginData),\n          with: request.with,\n        })\n\n        if (result != null) {\n          if (typeof result !== 'object') throw new Error(`Expected onLoad() callback in plugin ${quote(name)} to return an object`)\n          let keys: OptionKeys = {}\n          let pluginName = getFlag(result, keys, 'pluginName', mustBeString)\n          let contents = getFlag(result, keys, 'contents', mustBeStringOrUint8Array)\n          let resolveDir = getFlag(result, keys, 'resolveDir', mustBeString)\n          let pluginData = getFlag(result, keys, 'pluginData', canBeAnything)\n          let loader = getFlag(result, keys, 'loader', mustBeString)\n          let errors = getFlag(result, keys, 'errors', mustBeArray)\n          let warnings = getFlag(result, keys, 'warnings', mustBeArray)\n          let watchFiles = getFlag(result, keys, 'watchFiles', mustBeArrayOfStrings)\n          let watchDirs = getFlag(result, keys, 'watchDirs', mustBeArrayOfStrings)\n          checkForInvalidFlags(result, keys, `from onLoad() callback in plugin ${quote(name)}`)\n\n          response.id = id\n          if (pluginName != null) response.pluginName = pluginName\n          if (contents instanceof Uint8Array) response.contents = contents\n          else if (contents != null) response.contents = protocol.encodeUTF8(contents)\n          if (resolveDir != null) response.resolveDir = resolveDir\n          if (pluginData != null) response.pluginData = details.store(pluginData)","sourceCodeStart":1428,"sourceCodeEnd":1464,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/lib/shared/common.ts#L1428-L1464","documentation":"An onLoad callback returns { contents?, loader?, resolveDir?, errors?, warnings?, ... } describing the loaded module, or nothing to defer. lib/shared/common.ts:1446 throws when the return is a non-null non-object. Returning a bare string of source (a very common mistake) is rejected; contents must be wrapped in an object and must be a string or Uint8Array.","triggerScenarios":"onLoad(args => readFileSync(args.path, 'utf8')) — returns a string. onLoad(args => new Uint8Array(...)) — returns a Uint8Array directly. Returning a Promise<string>.","commonSituations":"Plugin that inlines virtual files returns the file text directly. Porting a loader whose convention was to return source as a string. Author forgets the wrapper { contents }.","solutions":["Wrap the contents: onLoad(args => ({ contents: readFileSync(args.path, 'utf8'), loader: 'ts' })).","For binary contents, return { contents: uint8array }.","Return undefined to let esbuild's default loader take over."],"exampleFix":"// before\nbuild.onLoad({ filter: /\\.txt$/ }, async args => {\n  return await fs.readFile(args.path, 'utf8');\n});\n// after\nbuild.onLoad({ filter: /\\.txt$/ }, async args => {\n  return { contents: await fs.readFile(args.path, 'utf8'), loader: 'text' };\n});","handlingStrategy":"validation","validationCode":"function wrapOnLoad(build, opts, cb) {\n  build.onLoad(opts, async (args) => {\n    const r = await cb(args);\n    if (r != null && typeof r !== 'object') {\n      throw new TypeError('onLoad callback must return { contents, loader?, ... } or void');\n    }\n    return r as any;\n  });\n}","typeGuard":"function isLoadResult(v): v is import('esbuild').OnLoadResult | null | undefined {\n  return v == null || (typeof v === 'object' && typeof (v as any).then !== 'function');\n}","tryCatchPattern":null,"preventionTips":["Always wrap source text: return { contents, loader }.","Return undefined to let esbuild's default loader handle the file.","Type the callback return as OnLoadResult | void."],"tags":["plugins","onload","return-value","validation"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}