oven-sh/bun · error · Error

generate-string-map: ${name}: emitCaseInsensitive requires A

Error message

generate-string-map: ${name}: emitCaseInsensitive requires ASCII keys; ${JSON.stringify(k)} is not

What it means

When a string-map spec sets `emitCaseInsensitive: true`, the generator also emits a `<name>_ignore_ascii_case` lookup that relies on ASCII-only case folding. The generator therefore validates every key against /^[\x00-\x7f]*$/ and refuses non-ASCII keys, because ASCII case folding would leave non-ASCII bytes uncomparable and produce a subtly broken lookup.

Source

Thrown at src/codegen/generate-string-map.ts:228

  }
  out.push(`        _ => None,`);
  out.push(`    }`);
  out.push(`}`);
}

function buildOne<V>(spec: StringMapSpec<V>): string {
  const { name, valueTy, entries, emitKeys, emitIndexOf, emitCaseInsensitive, doc } = spec;
  const emitValue = spec.emitValue ?? ((v: V) => (typeof v === "string" ? JSON.stringify(v) : String(v)));

  const seen = new Set<string>();
  const seenCi = new Set<string>();
  for (const [k] of entries) {
    if (seen.has(k)) throw new Error(`generate-string-map: duplicate key ${JSON.stringify(k)} in ${name}`);
    seen.add(k);
    if (emitCaseInsensitive) {
      // eslint-disable-next-line no-control-regex
      if (!/^[\x00-\x7f]*$/.test(k)) {
        throw new Error(
          `generate-string-map: ${name}: emitCaseInsensitive requires ASCII keys; ${JSON.stringify(k)} is not`,
        );
      }
      const lk = k.toLowerCase();
      if (seenCi.has(lk)) {
        throw new Error(`generate-string-map: ${name}: keys ${JSON.stringify(k)} collide under case folding`);
      }
      seenCi.add(lk);
    }
  }

  const kvs: Array<readonly [Buffer, string]> = entries.map(([k, v]) => [Buffer.from(k, "utf8"), emitValue(v)]);
  const out: string[] = [];
  if (doc) for (const line of doc.split("\n")) out.push(`/// ${line}`.trimEnd());
  emitLookup(out, name, valueTy, kvs, { ci: false });

  if (emitCaseInsensitive) {
    out.push(``);

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Replace the non-ASCII key the message prints with its pure-ASCII equivalent.
  2. If unicode keys are required, set `emitCaseInsensitive: false` and rely on the exact-match lookup only.
  3. Split the map: ASCII keys in the case-insensitive map, unicode keys in a separate exact-match map.

Example fix

// before
export default {
  name: "Header",
  emitCaseInsensitive: true,
  entries: [["café", 1]],
};

// after
export default {
  name: "Header",
  emitCaseInsensitive: true,
  entries: [["cafe", 1]],
};
Defensive patterns

Strategy: validation

Validate before calling

const isAsciiKey = k => /^[\x00-\x7f]*$/.test(k);
function assertAsciiKeys(name, entries, emitCaseInsensitive) {
  if (!emitCaseInsensitive) return;
  for (const [k] of entries) {
    if (!isAsciiKey(k)) throw new Error(`${name}: non-ASCII key ${JSON.stringify(k)} needs emitCaseInsensitive:false`);
  }
}

Prevention

When it happens

Trigger: A spec with `emitCaseInsensitive: true` whose entries contain any character above U+007F (e.g. "café", " size", a non-breaking space) or control characters outside the allowed range.

Common situations: Copying keys from a spec file saved with smart quotes, adding localized/unicode header or flag names to a case-insensitive lookup table.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/93c28329f29e6254. Report an issue: GitHub.