chenglou/pretext · error · Error

Unsupported bidi class ${raw}

Error message

Unsupported bidi class ${raw}

What it means

The derived file contains a bidi class abbreviation the generator does not model. Recognized inputs: the 14 short codes (L,R,AL,AN,EN,ES,ET,CS,ON,BN,B,S,WS,NSM), their long forms (e.g. `Arabic_Letter`), and the formatting/isolate controls (LRE,LRO,RLE,RLO,PDF,LRI,RLI,FSI,PDI) which project onto BN. Anything else throws.

Source

Thrown at scripts/generate-bidi-data.ts:85

function parseCodePointRange(raw: string): { start: number, end: number } {
  const [startRaw, endRaw] = raw.split('..')
  const start = Number.parseInt(startRaw!, 16)
  const end = endRaw === undefined ? start : Number.parseInt(endRaw, 16)
  if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) {
    throw new Error(`Invalid code point range: ${raw}`)
  }
  return { start, end }
}

function simplifyBidiType(raw: string): GeneratedBidiType {
  if (generatedTypeToCode.has(raw as GeneratedBidiType)) return raw as GeneratedBidiType
  if (bnProjectedTypes.has(raw)) return 'BN'

  const longName = longBidiNames.get(raw)
  if (longName !== undefined) return longName

  throw new Error(`Unsupported bidi class ${raw}`)
}

function buildPayload(sourceText: string): GeneratedBidiPayload {
  const sourceLines = sourceText.split(/\r?\n/)
  const versionLine = sourceLines.find(line => line.startsWith('# DerivedBidiClass-'))
  const versionMatch = versionLine?.match(/^# DerivedBidiClass-(.+)\.txt$/)
  if (versionMatch === null || versionMatch === undefined) {
    throw new Error('Could not determine Unicode version from DerivedBidiClass header')
  }
  const unicodeVersion = versionMatch[1]!

  const bidiCodes = new Uint8Array(0x110000)
  bidiCodes.fill(generatedTypeToCode.get('L')!)

  for (let i = 0; i < sourceLines.length; i++) {
    const rawLine = sourceLines[i]!
    let rangeText: string | null = null
    let typeText: string | null = null

View on GitHub (pinned to ac49b09b7d)

Solutions

  1. If the class is a new formatting/isolate control, add it to `bnProjectedTypes` so it projects onto BN.
  2. If it is a genuine new resolved class, extend `GENERATED_TYPES`, `longBidiNames`, and the generated type union — but first confirm the rich bidi helper actually needs it.
  3. Otherwise re-download a clean DerivedBidiClass file; a stray token can masquerade as a class.

Example fix

// before: a new isolate control (hypothetical 'RIN') is unsupported
const bnProjectedTypes = new Set(['LRE','LRO','RLE','RLO','PDF','LRI','RLI','FSI','PDI'])
// after: project the new control onto BN
const bnProjectedTypes = new Set(['LRE','LRO','RLE','RLO','PDF','LRI','RLI','FSI','PDI','RIN'])
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN = new Set([...GENERATED_TYPES, ...longBidiNames.keys(), ...bnProjectedTypes])
const unknown = new Set<string>()
for (const line of sourceLines) {
  const m = line.match(/;\s*([A-Za-z_]+)/)
  if (m && !KNOWN.has(m[1])) unknown.add(m[1])
}
if (unknown.size > 0) {
  console.error(`Unmodeled bidi classes: ${[...unknown].join(', ')}`)
  process.exit(1)
}

Type guard

const isRecognizedBidiClass = (raw: string): boolean =>
  generatedTypeToCode.has(raw as GeneratedBidiType) ||
  bnProjectedTypes.has(raw) ||
  longBidiNames.has(raw)

Prevention

When it happens

Trigger: A newer Unicode version introduces or surfaces a bidi class not in the projection set, or the source file carries an unexpected long name.

Common situations: Upgrading the bundled DerivedBidiClass to a new Unicode version without updating the generator's class tables.

Related errors


AI-assisted analysis of chenglou/pretext@ac49b09b7d (2026-08-12). Data as JSON: /api/errors/913710d6a41896c2. Report an issue: GitHub.