{"record":{"id":"8a948b585dcddfdc","repo":"microsoft/edit","slug":"unexpected-gcb-with-extpict-y-for-u-04x-to-u","errorCode":null,"errorMessage":"Unexpected GCB={} with ExtPict=Y for U+{:04X} to U+{:04X}","messagePattern":"Unexpected GCB=(.+?) with ExtPict=Y for U\\+(.+?) to U\\+(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/unicode-gen/src/main.rs","lineNumber":820,"sourceCode":"                \"L\" => ClusterBreak::HangulL,        // Hangul Syllable Type L\n                \"V\" => ClusterBreak::HangulV,        // Hangul Syllable Type V\n                \"T\" => ClusterBreak::HangulT,        // Hangul Syllable Type T\n                \"LV\" => ClusterBreak::HangulLV,      // Hangul Syllable Type LV\n                \"LVT\" => ClusterBreak::HangulLVT,    // Hangul Syllable Type LVT\n                _ => bail!(\n                    \"Unrecognized GCB={} for U+{:04X} to U+{:04X}\",\n                    char_attributes.grapheme_cluster_break,\n                    range.start(),\n                    range.end()\n                ),\n            };\n\n            if char_attributes.extended_pictographic == \"Y\" {\n                // Currently every single Extended_Pictographic codepoint happens to be GCB=XX.\n                // This is fantastic for us because it means we can stuff it into the ClusterBreak enum\n                // and treat it as an alias of EXTEND, but with the special GB11 properties.\n                if cb != ClusterBreak::Other {\n                    bail!(\n                        \"Unexpected GCB={} with ExtPict=Y for U+{:04X} to U+{:04X}\",\n                        char_attributes.grapheme_cluster_break,\n                        range.start(),\n                        range.end()\n                    );\n                }\n\n                cb = ClusterBreak::ExtPic;\n            }\n\n            if !matches!(char_attributes.indic_conjunct_break, \"None\" | \"Extend\") {\n                // If it's not None/Extend, it's Linker/Consonant, and currently\n                // all of them are GCB=EX/XX. Since we treat them almost like extenders,\n                // we need to revisit our assumptions if this ever changes.\n                if !matches!(cb, ClusterBreak::Other | ClusterBreak::Extend) {\n                    bail!(\n                        \"Unexpected GCB={} with InCB={} for U+{:04X} to U+{:04X}\",\n                        char_attributes.grapheme_cluster_break,","sourceCodeStart":802,"sourceCodeEnd":838,"githubUrl":"https://github.com/microsoft/edit/blob/826b4c097b6f14ba0a846dc56f2f0223a3aaf73a/crates/unicode-gen/src/main.rs#L802-L838","documentation":"The generator assumes every Extended_Pictographic codepoint has GCB=XX (Other) so it can alias ExtPict into the ClusterBreak enum with GB11 semantics. If a range has ExtPict=Y but a non-Other GCB, this invariant is broken and it bails rather than generating incorrect tables.","triggerScenarios":"A future/patched Unicode release where a codepoint range is both Extended_Pictographic and carries a concrete GCB value (CR/LF/Control/Extend/etc.); or corrupted emoji-data/GraphemeBreakProperty inputs whose ranges misalign.","commonSituations":"Regenerating tables against a newer Unicode version whose emoji-data.txt assignments changed; mixing files from different Unicode versions so ExtPict and GCB data disagree.","solutions":["Use a consistent, supported set of UCD files from a single Unicode version.","If a new Unicode release legitimately breaks the invariant, update the ClusterBreak modeling (no longer alias ExtPict into GCB) in the generator before regenerating.","Check the comment-documented assumption in main.rs and adjust the GB11 handling accordingly."],"exampleFix":"// before\nif cb != ClusterBreak::Other { bail!(\"Unexpected GCB=... with ExtPict=Y\") }\n// after\n// relax invariant: allow Extend with ExtPict=Y and handle GB11 explicitly\nif !matches!(cb, ClusterBreak::Other | ClusterBreak::Extend) { bail!(...) }","handlingStrategy":"try-catch","validationCode":"// verify ExtPict ranges align with GCB=XX before generating\ndownload_pinned_ucd(VERSION); // single consistent version for all property files\nverify_no_version_mixing(&[\"GraphemeBreakProperty.xml\", \"emoji-data.xml\"], VERSION)?;","typeGuard":null,"tryCatchPattern":"match generate(&ucd_path) {\n    Err(e) if e.to_string().contains(\"with ExtPict=Y\") => {\n        eprintln!(\"ExtPict invariant broken — UCD version mismatch or unsupported release: {e}\");\n        std::process::exit(1);\n    }\n    r => r?,\n}","preventionTips":["Always generate all tables from one single Unicode release's files.","Read the invariant comments in main.rs before bumping the Unicode version.","Keep emoji-data and GraphemeBreakProperty downloads in one pinned script."],"tags":["unicode","invariant","emoji-data"],"backgroundTag":"internal-invariant-violation","analyzedSha":"826b4c097b6f14ba0a846dc56f2f0223a3aaf73a","analyzedAt":"2026-09-06T13:30:05.543Z","contentChangedAt":"2026-09-06T13:30:05.543Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}