evanw/esbuild · error · Error
Cannot call "resolve" before plugin setup has completed
Error message
Cannot call "resolve" before plugin setup has completed
What it means
Inside a plugin's `setup`, esbuild provides a `resolve()` helper that calls back into esbuild's resolver. The closure (lib/shared/common.ts:1244) guards on `isSetupDone`; calling `resolve` synchronously during setup, before esbuild has finished initializing plugins, throws this error. `resolve` is meant to be used inside onResolve/onLoad/onStart/onEnd callbacks, or after setup completes.
Solutions
- Move the `resolve()` call inside an `onResolve`, `onLoad`, `onStart`, or `onEnd` callback where it is allowed.
- If you need resolution at setup time, defer it with an `onStart` hook instead of calling it synchronously in setup.
Example fix
// before
setup(build) {
build.resolve('./foo', { kind: 'import-statement' }); // throws
}
// after
setup(build) {
build.onStart(async () => {
const r = await build.resolve('./foo', { kind: 'import-statement' });
// use r
return null;
});
} Defensive patterns
Strategy: validation
Validate before calling
// Structural guard: never call build.resolve() at the top level of setup. // Only call it inside onStart/onResolve/onLoad/onEnd callbacks.
Prevention
- Treat the resolve() passed to setup as callback-only.
- Use onStart for setup-time resolution needs.
When it happens
Trigger: Calling `build.resolve(...)` (the argument passed to `setup`) synchronously in the setup body before any hook fires, e.g. `setup(build) { build.resolve('foo', {kind:'import-statement'}) }` at the top level of setup.
Common situations: Misunderstanding that the `resolve` passed to setup is for use inside callbacks; pre-resolving module paths during setup; moving logic out of an onResolve hook into setup.
Related errors
- Must specify "kind" when calling "resolve"
- onLoad() call is missing a filter
- onResolve() call is missing a filter
- Plugin at index must be an object
- Cannot serve a disposed context
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/0cb4c5e1ef3e7716.
Report an issue: GitHub.
Appendix: source
Thrown at lib/shared/common.ts:1244
if (typeof item !== 'object') throw new Error(`Plugin at index ${i} must be an object`)
const name = getFlag(item, keys, 'name', mustBeString)
if (typeof name !== 'string' || name === '') throw new Error(`Plugin at index ${i} is missing a name`)
try {
let setup = getFlag(item, keys, 'setup', mustBeFunction)
if (typeof setup !== 'function') throw new Error(`Plugin is missing a setup function`)
checkForInvalidFlags(item, keys, `on plugin ${quote(name)}`)
let plugin: protocol.BuildPlugin = {
name,
onStart: false,
onEnd: false,
onResolve: [],
onLoad: [],
}
i++
let resolve = (path: string, options: types.ResolveOptions = {}): Promise<types.ResolveResult> => {
if (!isSetupDone) throw new Error('Cannot call "resolve" before plugin setup has completed')
if (typeof path !== 'string') throw new Error(`The path to resolve must be a string`)
let keys: OptionKeys = Object.create(null)
let pluginName = getFlag(options, keys, 'pluginName', mustBeString)
let importer = getFlag(options, keys, 'importer', mustBeString)
let namespace = getFlag(options, keys, 'namespace', mustBeString)
let resolveDir = getFlag(options, keys, 'resolveDir', mustBeString)
let kind = getFlag(options, keys, 'kind', mustBeString)
let pluginData = getFlag(options, keys, 'pluginData', canBeAnything)
let importAttributes = getFlag(options, keys, 'with', mustBeObject)
checkForInvalidFlags(options, keys, 'in resolve() call')
return new Promise((resolve, reject) => {
const request: protocol.ResolveRequest = {
command: 'resolve',
path,
key: buildKey,
pluginName: name,
}View on GitHub (pinned to f6058f8364)