evanw/esbuild · error · Error

Expected onLoad() callback in plugin ${quote(name)} to retur

Error message

Expected onLoad() callback in plugin ${quote(name)} to return an object

What it means

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.

Source

Thrown at lib/shared/common.ts:1446

    }
    sendResponse(id, response as any)
  }

  requestCallbacks['on-load'] = async (id, request: protocol.OnLoadRequest) => {
    let response: protocol.OnLoadResponse = {}, name = '', callback, note
    for (let id of request.ids) {
      try {
        ({ name, callback, note } = onLoadCallbacks[id])
        let result = await callback({
          path: request.path,
          namespace: request.namespace,
          suffix: request.suffix,
          pluginData: details.load(request.pluginData),
          with: request.with,
        })

        if (result != null) {
          if (typeof result !== 'object') throw new Error(`Expected onLoad() callback in plugin ${quote(name)} to return an object`)
          let keys: OptionKeys = {}
          let pluginName = getFlag(result, keys, 'pluginName', mustBeString)
          let contents = getFlag(result, keys, 'contents', mustBeStringOrUint8Array)
          let resolveDir = getFlag(result, keys, 'resolveDir', mustBeString)
          let pluginData = getFlag(result, keys, 'pluginData', canBeAnything)
          let loader = getFlag(result, keys, 'loader', mustBeString)
          let errors = getFlag(result, keys, 'errors', mustBeArray)
          let warnings = getFlag(result, keys, 'warnings', mustBeArray)
          let watchFiles = getFlag(result, keys, 'watchFiles', mustBeArrayOfStrings)
          let watchDirs = getFlag(result, keys, 'watchDirs', mustBeArrayOfStrings)
          checkForInvalidFlags(result, keys, `from onLoad() callback in plugin ${quote(name)}`)

          response.id = id
          if (pluginName != null) response.pluginName = pluginName
          if (contents instanceof Uint8Array) response.contents = contents
          else if (contents != null) response.contents = protocol.encodeUTF8(contents)
          if (resolveDir != null) response.resolveDir = resolveDir
          if (pluginData != null) response.pluginData = details.store(pluginData)

View on GitHub (pinned to 6ff1d8b0d8)

Solutions

  1. Wrap the contents: onLoad(args => ({ contents: readFileSync(args.path, 'utf8'), loader: 'ts' })).
  2. For binary contents, return { contents: uint8array }.
  3. Return undefined to let esbuild's default loader take over.

Example fix

// before
build.onLoad({ filter: /\.txt$/ }, async args => {
  return await fs.readFile(args.path, 'utf8');
});
// after
build.onLoad({ filter: /\.txt$/ }, async args => {
  return { contents: await fs.readFile(args.path, 'utf8'), loader: 'text' };
});
Defensive patterns

Strategy: validation

Validate before calling

function wrapOnLoad(build, opts, cb) {
  build.onLoad(opts, async (args) => {
    const r = await cb(args);
    if (r != null && typeof r !== 'object') {
      throw new TypeError('onLoad callback must return { contents, loader?, ... } or void');
    }
    return r as any;
  });
}

Type guard

function isLoadResult(v): v is import('esbuild').OnLoadResult | null | undefined {
  return v == null || (typeof v === 'object' && typeof (v as any).then !== 'function');
}

Prevention

When it happens

Trigger: onLoad(args => readFileSync(args.path, 'utf8')) — returns a string. onLoad(args => new Uint8Array(...)) — returns a Uint8Array directly. Returning a Promise<string>.

Common situations: 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 }.

Related errors


AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03). Data as JSON: /data/errors/d9c3d274a01858bc.json. Report an issue: GitHub.