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
- Return an object such as `{ contents: sourceText, loader: 'js', resolveDir: dir }`, or `null` to defer.
- 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
- Wrap contents in { contents, loader }.
- Return null to defer to the next plugin.
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
- Expected onEnd() callback in plugin
- Expected onResolve() callback in plugin
- Expected onStart() callback in plugin
- onLoad() call is missing a filter
- key in object must be a string
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)