{"record":{"id":"f0d60f2a0f1912a4","repo":"evanw/esbuild","slug":"quote-property-must-be-an-array-of-strings","errorCode":null,"errorMessage":"${quote(property)} must be an array of strings","messagePattern":"(.+?) must be an array of strings","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1816,"sourceCode":"\n    messagesClone.push({\n      id: id || '',\n      pluginName: pluginName || fallbackPluginName,\n      text: text || '',\n      location: sanitizeLocation(location, where, terminalWidth),\n      notes: notesClone,\n      detail: stash ? stash.store(detail) : -1,\n    })\n    index++\n  }\n\n  return messagesClone\n}\n\nfunction sanitizeStringArray(values: any[], property: string): string[] {\n  const result: string[] = []\n  for (const value of values) {\n    if (typeof value !== 'string') throw new Error(`${quote(property)} must be an array of strings`)\n    result.push(value)\n  }\n  return result\n}\n\nfunction sanitizeStringMap(map: Record<string, any>, property: string): Record<string, string> {\n  const result: Record<string, string> = Object.create(null)\n  for (const key in map) {\n    const value = map[key]\n    if (typeof value !== 'string') throw new Error(`key ${quote(key)} in object ${quote(property)} must be a string`)\n    result[key] = value\n  }\n  return result\n}\n\nfunction convertOutputFiles({ path, contents, hash }: protocol.BuildOutputFile): types.OutputFile {\n  // The text is lazily-generated for performance reasons. If no one asks for\n  // it, then it never needs to be generated.","sourceCodeStart":1798,"sourceCodeEnd":1834,"githubUrl":"https://github.com/evanw/esbuild/blob/f6058f8364fe7ab91ca57a83e02577ed74c9cae4/lib/shared/common.ts#L1798-L1834","documentation":"The helper `sanitizeStringArray` (lib/shared/common.ts:1813) is used to validate `watchFiles` and `watchDirs` returned by onResolve/onLoad callbacks. It iterates the array and throws (lib/shared/common.ts:1816) for any element whose `typeof` is not `'string'`. The quoted property name tells you which field was malformed.","triggerScenarios":"Returning `watchFiles` or `watchDirs` from an onResolve/onLoad callback where at least one entry is a number, object, null, or undefined instead of a string.","commonSituations":"Pushing a path object (e.g. `{ path }`) instead of the string path; mixing in `undefined` from an optional field; returning file descriptors instead of paths.","solutions":["Ensure every element of `watchFiles`/`watchDirs` is a string (e.g. `watchFiles: files.map(f => f.path)`).","Filter out nullish entries before returning: `arr.filter(Boolean)`."],"exampleFix":"// before\nreturn { contents, watchFiles: [{ path: depPath }] };\n// after\nreturn { contents, watchFiles: [depPath] };","handlingStrategy":"validation","validationCode":"function cleanStringArray(arr: any[]): string[] {\n  return arr.map((v, i) => { if (typeof v !== 'string') throw new Error(`element ${i} is not a string`); return v; });\n}","typeGuard":"function isStringArray(a: any): a is string[] {\n  return Array.isArray(a) && a.every(v => typeof v === 'string');\n}","tryCatchPattern":null,"preventionTips":["Map path objects to strings before returning watchFiles/watchDirs.","Filter nullish entries with arr.filter(x => typeof x === 'string')."],"tags":["plugins","validation","watch","data-shape"],"backgroundTag":null,"analyzedSha":"f6058f8364fe7ab91ca57a83e02577ed74c9cae4","analyzedAt":"2026-08-09T18:37:22.223Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}