evanw/esbuild · error · Error
Must specify "kind" when calling "resolve"
Error message
Must specify "kind" when calling "resolve"
What it means
Inside a plugin, build.resolve(path, options) forwards to esbuild's resolver; the 'kind' option tells it the import context (import-statement, entry-point, require-resolve, etc.) because resolution rules differ by kind (e.g. ESM vs CJS). At lib/shared/common.ts:1264 esbuild throws if 'kind' is omitted, since guessing would silently pick the wrong resolution semantics and produce wrong external/path results.
Source
Thrown at lib/shared/common.ts:1264
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,
}
if (pluginName != null) request.pluginName = pluginName
if (importer != null) request.importer = importer
if (namespace != null) request.namespace = namespace
if (resolveDir != null) request.resolveDir = resolveDir
if (kind != null) request.kind = kind
else throw new Error(`Must specify "kind" when calling "resolve"`)
if (pluginData != null) request.pluginData = details.store(pluginData)
if (importAttributes != null) request.with = sanitizeStringMap(importAttributes, 'with')
sendRequest<protocol.ResolveRequest, protocol.ResolveResponse>(refs, request, (error, response) => {
if (error !== null) reject(new Error(error))
else resolve({
errors: replaceDetailsInMessages(response!.errors, details),
warnings: replaceDetailsInMessages(response!.warnings, details),
path: response!.path,
external: response!.external,
sideEffects: response!.sideEffects,
namespace: response!.namespace,
suffix: response!.suffix,
pluginData: details.load(response!.pluginData),
})
})
})
}View on GitHub (pinned to 6ff1d8b0d8)
Solutions
- Always pass kind: one of 'entry-point' | 'import-statement' | 'require-call' | 'dynamic-import' | 'require-resolve' | 'css-rule' | 'css-at-rule' | 'js-url' | 'css-url' | 'css-import' | 'json-import'.
- Match the kind to how the import appears in source: import x from → 'import-statement', require() → 'require-call'.
- Add a TS helper that asserts kind is present: type ResolveKind = ...; and a runtime check.
Example fix
// before
const r = await build.resolve('./mod', { importer: file });
// after
const r = await build.resolve('./mod', { importer: file, kind: 'import-statement' }); Defensive patterns
Strategy: validation
Validate before calling
const RESOLVE_KINDS = new Set([
'entry-point','import-statement','require-call','dynamic-import',
'require-resolve','css-rule','css-at-rule','css-url','css-import',
'js-url','json-import',
]);
function safeResolve(build, path, opts) {
if (!opts?.kind || !RESOLVE_KINDS.has(opts.kind)) {
throw new TypeError('build.resolve requires opts.kind from the supported set');
}
return build.resolve(path, opts);
} Type guard
function isResolveKind(v): v is import('esbuild').ResolveKind {
return typeof v === 'string' && [
'entry-point','import-statement','require-call','dynamic-import',
'require-resolve','css-rule','css-at-rule','css-url','css-import',
'js-url','json-import',
].includes(v);
} Prevention
- Always pass kind when calling build.resolve; match it to the import syntax in source.
- Write a small helper that asserts kind to centralise the rule.
- Keep the kind set in sync with the installed esbuild version's types.
When it happens
Trigger: Calling build.resolve('./foo') with no options. Calling build.resolve('./foo', { importer }) forgetting kind. Passing kind: undefined explicitly.
Common situations: Plugin does custom resolution and copies a partial options object. Refactor that drops the kind field. Devs reading the type signature ResolveOptions and assuming kind is optional (it's optional in the type but required at runtime for this codepath).
Related errors
- Plugin at index ${i} must be an object
- Plugin at index ${i} is missing a name
- Plugin is missing a setup function
- Cannot call "resolve" before plugin setup has completed
- onResolve() call is missing a filter
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/4357dea1952cb2c0.json.
Report an issue: GitHub.