{"id":"616cd557bc8c5eb8","repo":"evanw/esbuild","slug":"key-quote-key-in-object-quote-property-mus","errorCode":null,"errorMessage":"key ${quote(key)} in object ${quote(property)} must be a string","messagePattern":"key (.+?) in object (.+?) must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/shared/common.ts","lineNumber":1822,"sourceCode":"  }\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.\n  let text: string | null = null\n  return {\n    path,\n    contents,\n    hash,\n    get text() {\n      // People want to be able to set \"contents\" and have esbuild automatically\n      // derive \"text\" for them, so grab the contents off of this object instead\n      // of using our original value.\n      const binary = this.contents","sourceCodeStart":1804,"sourceCodeEnd":1840,"githubUrl":"https://github.com/evanw/esbuild/blob/6ff1d8b0d8c134e867a397eef39702a223ebef9e/lib/shared/common.ts#L1804-L1840","documentation":"Import attributes (the 'with' option in resolve(), or the with field from onResolve/onLoad) must be a map of string→string because esbuild forwards them verbatim to the native resolver over the stdio protocol, which only accepts string values. sanitizeStringMap (lib/shared/common.ts:1818) throws at :1822 when any value is not a string — e.g. with: { type: 'json' } is fine, but with: { count: 3 } throws. The message includes the offending key and the parent property name.","triggerScenarios":"resolve(path, { kind, with: { type: 'json', retries: 5 } }) — retries is a number. onLoad returning { contents, with: { ...nonString } }. Plugin forwards arbitrary user metadata into with.","commonSituations":"Treating 'with' as a generic metadata bag instead of an import-attributes map. Migration from the deprecated assert syntax where values happened to be strings. Plugin authors passing through JSON from a config that has numbers/booleans.","solutions":["Stringify all values: with: Object.fromEntries(Object.entries(attrs).map(([k,v]) => [k, String(v)])).","Only put import attributes (like type: 'json') into 'with'; route other metadata via pluginData.","Validate at plugin boundary: for (const k in with_) if (typeof with_[k] !== 'string') throw."],"exampleFix":"// before\nbuild.resolve('./data.json', {\n  kind: 'import-statement',\n  with: { type: 'json', priority: 1 },\n});\n// after\nbuild.resolve('./data.json', {\n  kind: 'import-statement',\n  with: { type: 'json', priority: '1' },\n});","handlingStrategy":"validation","validationCode":"function asStringMap(v, name) {\n  const out = Object.create(null);\n  for (const k in v) {\n    if (typeof v[k] !== 'string') {\n      throw new TypeError(`key ${JSON.stringify(k)} in object ${JSON.stringify(name)} must be a string`);\n    }\n    out[k] = v[k];\n  }\n  return out;\n}","typeGuard":"function isStringMap(v): v is Record<string, string> {\n  return !!v && typeof v === 'object' && !Array.isArray(v)\n    && Object.values(v).every(x => typeof x === 'string');\n}","tryCatchPattern":null,"preventionTips":["Only put import attributes (like type: 'json') into the 'with' map; route other metadata via pluginData.","Stringify non-string values before placing them in 'with'.","Validate plugin-supplied import attributes at the plugin boundary."],"tags":["plugins","import-attributes","validation"],"analyzedSha":"6ff1d8b0d8c134e867a397eef39702a223ebef9e","analyzedAt":"2026-08-03T19:42:38.433Z","schemaVersion":2}