{"record":{"id":"f2f9d303dcda5982","repo":"chenglou/pretext","slug":"invalid-code-point-range-raw","errorCode":null,"errorMessage":"Invalid code point range: ${raw}","messagePattern":"Invalid code point range: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/generate-bidi-data.ts","lineNumber":73,"sourceCode":"  'RLE',\n  'RLO',\n  'PDF',\n  'LRI',\n  'RLI',\n  'FSI',\n  'PDI',\n])\n\nfunction formatHex(value: number): string {\n  return `0x${value.toString(16).toUpperCase()}`\n}\n\nfunction parseCodePointRange(raw: string): { start: number, end: number } {\n  const [startRaw, endRaw] = raw.split('..')\n  const start = Number.parseInt(startRaw!, 16)\n  const end = endRaw === undefined ? start : Number.parseInt(endRaw, 16)\n  if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) {\n    throw new Error(`Invalid code point range: ${raw}`)\n  }\n  return { start, end }\n}\n\nfunction simplifyBidiType(raw: string): GeneratedBidiType {\n  if (generatedTypeToCode.has(raw as GeneratedBidiType)) return raw as GeneratedBidiType\n  if (bnProjectedTypes.has(raw)) return 'BN'\n\n  const longName = longBidiNames.get(raw)\n  if (longName !== undefined) return longName\n\n  throw new Error(`Unsupported bidi class ${raw}`)\n}\n\nfunction buildPayload(sourceText: string): GeneratedBidiPayload {\n  const sourceLines = sourceText.split(/\\r?\\n/)\n  const versionLine = sourceLines.find(line => line.startsWith('# DerivedBidiClass-'))\n  const versionMatch = versionLine?.match(/^# DerivedBidiClass-(.+)\\.txt$/)","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/chenglou/pretext/blob/ac49b09b7d83ede19581fa94a8b892b07d309baf/scripts/generate-bidi-data.ts#L55-L91","documentation":"Thrown while parsing a hex code-point range line from `scripts/unicode/DerivedBidiClass-<version>.txt`. Fails if start/end aren't valid hex integers, start is negative, or end < start. Ranges look like `0041..005A` or a single `0041`.","triggerScenarios":"The bundled DerivedBidiClass file is replaced with one whose range column isn't well-formed hex, or the buildPayload regex lets a malformed line through to parseCodePointRange.","commonSituations":"Upgrading the Unicode source file to a version with a format change; hand-editing the derived file; pointing the script at a truncated or corrupted download.","solutions":["Re-download `DerivedBidiClass-<version>.txt` from the Unicode UCD; do not hand-edit it.","If upgrading Unicode, confirm the file format is unchanged; the parser expects `HEX[..HEX] ; CLASS`.","Inspect the `raw` value in the message — it pinpoints the offending line."],"exampleFix":"// before: hand-edited range with a typo in the hex end\n0530..053F ;  R   # ARMENIAN\n// after: valid hex range (re-download from UCD)\n0530..058F ;  R   # ARMENIAN","handlingStrategy":"validation","validationCode":"// sanity-check every range token in the derived file before generating\nfor (const line of sourceLines) {\n  const m = line.match(/^([0-9A-Fa-f]+(?:\\.\\.[0-9A-Fa-f]+)?)/)\n  if (!m) continue\n  const [s, e] = m[1].split('..')\n  const start = parseInt(s, 16), end = parseInt(e ?? s, 16)\n  if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) {\n    console.error(`Bad range line: ${line}`)\n    process.exit(1)\n  }\n}","typeGuard":"const isValidHexRange = (raw: string): boolean => {\n  const m = raw.match(/^([0-9A-Fa-f]+)(?:\\.\\.([0-9A-Fa-f]+))?$/)\n  if (!m) return false\n  const start = parseInt(m[1], 16), end = parseInt(m[2] ?? m[1], 16)\n  return Number.isInteger(start) && Number.isInteger(end) && start >= 0 && end >= start\n}","tryCatchPattern":null,"preventionTips":["Never hand-edit scripts/unicode/DerivedBidiClass-*.txt; always re-download from the UCD.","After a Unicode upgrade, run the generator and watch the raw value in any failure to find the offending line.","Keep the DerivedBidiClass filename in sync with the version pinned in generate-bidi-data.ts."],"tags":["unicode","bidi","codegen","data-file","validation"],"backgroundTag":null,"analyzedSha":"ac49b09b7d83ede19581fa94a8b892b07d309baf","analyzedAt":"2026-08-12T17:03:16.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}