{"record":{"id":"8b5fc2704fb0305b","repo":"oven-sh/bun","slug":"generate-string-map-name-keys-json-stringif","errorCode":null,"errorMessage":"generate-string-map: ${name}: keys ${JSON.stringify(k)} collide under case folding","messagePattern":"generate-string-map: (.+?): keys (.+?) collide under case folding","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/codegen/generate-string-map.ts","lineNumber":234,"sourceCode":"function buildOne<V>(spec: StringMapSpec<V>): string {\n  const { name, valueTy, entries, emitKeys, emitIndexOf, emitCaseInsensitive, doc } = spec;\n  const emitValue = spec.emitValue ?? ((v: V) => (typeof v === \"string\" ? JSON.stringify(v) : String(v)));\n\n  const seen = new Set<string>();\n  const seenCi = new Set<string>();\n  for (const [k] of entries) {\n    if (seen.has(k)) throw new Error(`generate-string-map: duplicate key ${JSON.stringify(k)} in ${name}`);\n    seen.add(k);\n    if (emitCaseInsensitive) {\n      // eslint-disable-next-line no-control-regex\n      if (!/^[\\x00-\\x7f]*$/.test(k)) {\n        throw new Error(\n          `generate-string-map: ${name}: emitCaseInsensitive requires ASCII keys; ${JSON.stringify(k)} is not`,\n        );\n      }\n      const lk = k.toLowerCase();\n      if (seenCi.has(lk)) {\n        throw new Error(`generate-string-map: ${name}: keys ${JSON.stringify(k)} collide under case folding`);\n      }\n      seenCi.add(lk);\n    }\n  }\n\n  const kvs: Array<readonly [Buffer, string]> = entries.map(([k, v]) => [Buffer.from(k, \"utf8\"), emitValue(v)]);\n  const out: string[] = [];\n  if (doc) for (const line of doc.split(\"\\n\")) out.push(`/// ${line}`.trimEnd());\n  emitLookup(out, name, valueTy, kvs, { ci: false });\n\n  if (emitCaseInsensitive) {\n    out.push(``);\n    emitLookup(out, `${name}_ignore_ascii_case`, valueTy, kvs, { ci: true });\n  }\n\n  if (emitIndexOf) {\n    // Index in *declaration order* — same as `<NAME>_KEYS` so\n    // `KEYS[index_of(k).unwrap()]` round-trips. u16 is plenty (asserted).","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/codegen/generate-string-map.ts#L216-L252","documentation":"With `emitCaseInsensitive: true`, the generator lowercases every key and keeps a `seenCi` set. Two distinct keys that fold to the same lowercase form (\"Content-Type\" and \"content-type\") would be indistinguishable to the generated `<name>_ignore_ascii_case` match, so the generator aborts instead of emitting ambiguous arms.","triggerScenarios":"A case-insensitive spec containing two entries whose keys differ only in ASCII letter case.","commonSituations":"Adding an all-lowercase HTTP header name next to its canonical mixed-case form; merging tables from two sources that used different casing conventions.","solutions":["Delete one of the colliding entries (keep the canonical casing) — the case-insensitive lookup matches both anyway.","Rename one key so the lowercase forms differ.","Turn off `emitCaseInsensitive` if the two casings must map to different values."],"exampleFix":"// before\nemitCaseInsensitive: true,\nentries: [\n  [\"Content-Type\", A],\n  [\"content-type\", B], // collides under case folding\n],\n\n// after\nemitCaseInsensitive: true,\nentries: [[\"Content-Type\", A]],","handlingStrategy":"validation","validationCode":"function assertNoCaseCollisions(name, entries, emitCaseInsensitive) {\n  if (!emitCaseInsensitive) return;\n  const seenCi = new Set();\n  for (const [k] of entries) {\n    const lk = k.toLowerCase();\n    if (seenCi.has(lk)) throw new Error(`${name}: ${k} collides under case folding`);\n    seenCi.add(lk);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one canonical casing convention (e.g. all-lowercase) for the whole table.","When merging tables from multiple sources, normalize casing during the merge, not after.","Remember the case-insensitive lookup makes casing variants redundant — never add both."],"tags":["codegen","string-map","case-insensitive"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}