evanw/esbuild · error · Error
Expected onStart() callback in plugin ${quote(name)} to retu
Error message
Expected onStart() callback in plugin ${quote(name)} to return an object What it means
An onStart callback may return either nothing (undefined/null) or an object shaped like { errors?, warnings? } so esbuild can inject messages into the build. lib/shared/common.ts:1363 throws when the callback returns a non-null, non-object value (e.g. a string, number, boolean, array). Returning an array specifically fails because typeof [] === 'object' is true, but other primitives like a string or true trigger this.
Source
Thrown at lib/shared/common.ts:1363
return { ok: false, error: e, pluginName: name }
}
}
requestCallbacks['on-start'] = async (id, request: protocol.OnStartRequest) => {
// Reset the "pluginData" map before each new build to avoid a memory leak.
// This is done before each new build begins instead of after each build ends
// because I believe the current API doesn't restrict when you can call
// "resolve" and there may be some uses of it that call it around when the
// build ends, and we don't want to accidentally break those use cases.
details.clear()
let response: protocol.OnStartResponse = { errors: [], warnings: [] }
await Promise.all(onStartCallbacks.map(async ({ name, callback, note }) => {
try {
let result = await callback()
if (result != null) {
if (typeof result !== 'object') throw new Error(`Expected onStart() callback in plugin ${quote(name)} to return an object`)
let keys: OptionKeys = {}
let errors = getFlag(result, keys, 'errors', mustBeArray)
let warnings = getFlag(result, keys, 'warnings', mustBeArray)
checkForInvalidFlags(result, keys, `from onStart() callback in plugin ${quote(name)}`)
if (errors != null) response.errors!.push(...sanitizeMessages(errors, 'errors', details, name, undefined))
if (warnings != null) response.warnings!.push(...sanitizeMessages(warnings, 'warnings', details, name, undefined))
}
} catch (e) {
response.errors!.push(extractErrorMessageV8(e, streamIn, details, note && note(), name))
}
}))
sendResponse(id, response as any)
}
requestCallbacks['on-resolve'] = async (id, request: protocol.OnResolveRequest) => {
let response: protocol.OnResolveResponse = {}, name = '', callback, note
for (let id of request.ids) {View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Return either undefined/null or an object: onStart(() => { doWork(); }) or onStart(() => ({ warnings: [...] })).
- If you need to surface a problem, return { errors: [{ text: '...', location }] }.
- Make async callbacks return Promise<{ errors?: ...; warnings?: ... } | void>.
Example fix
// before
build.onStart(async () => { await precache(); return 'cached'; });
// after
build.onStart(async () => { await precache(); }); Defensive patterns
Strategy: validation
Validate before calling
function onStartSafe(build, cb) {
build.onStart(async () => {
const r = await cb();
if (r != null && (typeof r !== 'object' || Array.isArray(r))) {
throw new TypeError('onStart callback must return { errors?, warnings? } or void');
}
return r as any;
});
} Type guard
function isStartResult(v): v is { errors?: any[]; warnings?: any[] } {
return v == null || (typeof v === 'object' && !Array.isArray(v));
} Prevention
- Return either undefined or { errors, warnings } from onStart.
- Don't return status strings/booleans from async onStart callbacks.
- Type the callback as () => Promise<void | { errors?: ...; warnings?: ... }>.
When it happens
Trigger: Plugin's onStart returns a string 'done' or a number status. Returning a Promise<string> (async callback returning a bare value). Returning an Error object directly instead of { errors: [...] }.
Common situations: Async plugin whose onStart resolves to a status string the author intended to log. Refactor where the callback returns the raw result of a helper that returns a primitive. Porting a webpack plugin whose hook returns a boolean.
Related errors
- Expected onResolve() callback in plugin ${quote(name)} to re
- Expected onLoad() callback in plugin ${quote(name)} to retur
- Expected onEnd() callback in plugin ${quote(name)} to return
- Plugin at index ${i} must be an object
- Plugin at index ${i} is missing a name
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/d62e2314485a315f.json.
Report an issue: GitHub.