evanw/esbuild · error · Error

Expected onLoad() callback in plugin

Error message

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

What it means

When an `onLoad` callback runs (lib/shared/common.ts:1450), its awaited return value must be `null`/`undefined` or an object with fields like `contents`, `loader`, `resolveDir`, `errors`, `warnings`. A truthy non-object (e.g. a string of source code, a Uint8Array, a number) triggers this error.

Solutions

  1. Return an object such as `{ contents: sourceText, loader: 'js', resolveDir: dir }`, or `null` to defer.
  2. Wrap any raw contents (string or Uint8Array) inside the `contents` field.

Example fix

// before
build.onLoad({ filter: /.*/ }, args => fs.readFileSync(args.path, 'utf8'));
// after
build.onLoad({ filter: /.*/ }, args => ({ contents: fs.readFileSync(args.path, 'utf8'), loader: 'ts' }));
Defensive patterns

Strategy: type-guard

Validate before calling

function wrapOnLoad(cb) {
  return async (args) => { const r = await cb(args); if (r != null && typeof r !== 'object') throw new Error('onLoad must return object or null'); return r; };
}

Type guard

function isOnLoadResult(v: any): v is { contents?: string | Uint8Array; loader?: string } | null {
  return v == null || (typeof v === 'object' && !Array.isArray(v));
}

Prevention

When it happens

Trigger: An onLoad callback that returns a string of file contents directly instead of `{ contents: ... }`.

Common situations: Returning raw source text; returning the file contents Buffer; returning a boolean to indicate success.

Related errors


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

Appendix: source

Thrown at lib/shared/common.ts:1450

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