{"id":"d62e2314485a315f","repo":"evanw/esbuild","slug":"expected-onstart-callback-in-plugin-quote-name","errorCode":null,"errorMessage":"Expected onStart() callback in plugin ${quote(name)} to return an object","messagePattern":"Expected onStart\\(\\) callback in plugin (.+?) to return an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1363,"sourceCode":"      return { ok: false, error: e, pluginName: name }\n    }\n  }\n\n  requestCallbacks['on-start'] = async (id, request: protocol.OnStartRequest) => {\n    // Reset the \"pluginData\" map before each new build to avoid a memory leak.\n    // This is done before each new build begins instead of after each build ends\n    // because I believe the current API doesn't restrict when you can call\n    // \"resolve\" and there may be some uses of it that call it around when the\n    // build ends, and we don't want to accidentally break those use cases.\n    details.clear()\n\n    let response: protocol.OnStartResponse = { errors: [], warnings: [] }\n    await Promise.all(onStartCallbacks.map(async ({ name, callback, note }) => {\n      try {\n        let result = await callback()\n\n        if (result != null) {\n          if (typeof result !== 'object') throw new Error(`Expected onStart() callback in plugin ${quote(name)} to return an object`)\n          let keys: OptionKeys = {}\n          let errors = getFlag(result, keys, 'errors', mustBeArray)\n          let warnings = getFlag(result, keys, 'warnings', mustBeArray)\n          checkForInvalidFlags(result, keys, `from onStart() callback in plugin ${quote(name)}`)\n\n          if (errors != null) response.errors!.push(...sanitizeMessages(errors, 'errors', details, name, undefined))\n          if (warnings != null) response.warnings!.push(...sanitizeMessages(warnings, 'warnings', details, name, undefined))\n        }\n      } catch (e) {\n        response.errors!.push(extractErrorMessageV8(e, streamIn, details, note && note(), name))\n      }\n    }))\n    sendResponse(id, response as any)\n  }\n\n  requestCallbacks['on-resolve'] = async (id, request: protocol.OnResolveRequest) => {\n    let response: protocol.OnResolveResponse = {}, name = '', callback, note\n    for (let id of request.ids) {","sourceCodeStart":1345,"sourceCodeEnd":1381,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/lib/shared/common.ts#L1345-L1381","documentation":"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.","triggerScenarios":"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: [...] }.","commonSituations":"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.","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>."],"exampleFix":"// before\nbuild.onStart(async () => { await precache(); return 'cached'; });\n// after\nbuild.onStart(async () => { await precache(); });","handlingStrategy":"validation","validationCode":"function onStartSafe(build, cb) {\n  build.onStart(async () => {\n    const r = await cb();\n    if (r != null && (typeof r !== 'object' || Array.isArray(r))) {\n      throw new TypeError('onStart callback must return { errors?, warnings? } or void');\n    }\n    return r as any;\n  });\n}","typeGuard":"function isStartResult(v): v is { errors?: any[]; warnings?: any[] } {\n  return v == null || (typeof v === 'object' && !Array.isArray(v));\n}","tryCatchPattern":null,"preventionTips":["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?: ... }>."],"tags":["plugins","onstart","return-value","validation"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}