{"record":{"id":"d8399ab564126582","repo":"iOfficeAI/OfficeCLI","slug":"propname-contains-xml-illegal-control-character","errorCode":null,"errorMessage":"{propName} contains XML-illegal control character U+{(int)c:X4} at position {i}. Allowed control chars: \\t, \\n, \\r{allowSoftBreakChar ? \", \\v.\" : \".\"}","messagePattern":"(.+?) contains XML-illegal control character U\\+(.+?) at position (.+?)\\. Allowed control chars: \\\\t, \\\\n, \\\\r(.+?)","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/ParseHelpers.cs","lineNumber":851,"sourceCode":"    /// (U+D800–U+DFFF without a matching pair), and the U+FFFE / U+FFFF\n    /// noncharacters.\n    /// </summary>\n    public static void ValidateXmlText(string? value, string propName, bool allowSoftBreakChar = false)\n    {\n        if (value == null) return;\n        for (int i = 0; i < value.Length; i++)\n        {\n            char c = value[i];\n            if (c == '\\t' || c == '\\n' || c == '\\r') continue;\n            // '\\v' (0x0B) is XML-illegal as character data. It is allowed ONLY\n            // when the caller consumes it into a break ELEMENT before\n            // serialization (NEWLINE-SEMANTICS-V2: AppendTextWithBreaks turns\n            // '\\v' into <w:br/>). Callers that write validated text verbatim\n            // into XML (chart titles, xlsx cell values, headers, ...) keep the\n            // strict default so '\\v' can never reach raw character data.\n            if (c == '\\v' && allowSoftBreakChar) continue;\n            if (c < 0x20)\n                throw new ArgumentException(\n                    $\"{propName} contains XML-illegal control character U+{(int)c:X4} at position {i}. \" +\n                    \"Allowed control chars: \\\\t, \\\\n, \\\\r\" +\n                    (allowSoftBreakChar ? \", \\\\v.\" : \".\"));\n            // UTF-16 surrogates only valid in pairs (high then low). A lone\n            // half is illegal in XML 1.0 character data.\n            if (char.IsHighSurrogate(c))\n            {\n                if (i + 1 >= value.Length || !char.IsLowSurrogate(value[i + 1]))\n                    throw new ArgumentException(\n                        $\"{propName} contains an unpaired high surrogate U+{(int)c:X4} at position {i}. Use a complete UTF-16 surrogate pair.\");\n                i++; // skip the matched low surrogate\n                continue;\n            }\n            if (char.IsLowSurrogate(c))\n                throw new ArgumentException(\n                    $\"{propName} contains an unpaired low surrogate U+{(int)c:X4} at position {i}. Use a complete UTF-16 surrogate pair.\");\n            if (c == 0xFFFE || c == 0xFFFF)\n                throw new ArgumentException(","sourceCodeStart":833,"sourceCodeEnd":869,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/ParseHelpers.cs#L833-L869","documentation":"Thrown by ParseHelpers.ValidateXmlText when the value contains an XML 1.0 illegal control character (U+0000–U+0008, U+000B, U+000C, U+000E–U+001F). Only \\t, \\n, \\r are always allowed; \\v (U+000B) is allowed only when allowSoftBreakChar=true (callers that turn it into a <w:br/> before serialization). This pre-validates so the OOXML serializer does not fail later at save time with a data-loss message.","triggerScenarios":"Setting any text that flows verbatim into XML character data — chart/axis titles, cell values, headers/footers, comments, bookmarks, field instructions, hyperlinks, image alt text, number format codes — and the string contains a raw control char like a NUL (U+0000), BEL (U+0007), or a vertical tab \\v when the caller kept the strict default.","commonSituations":"Pasting text from a terminal/database that contains NUL or other C0 control bytes; embedded \\v (vertical tab) in a field that does not convert it to a break element; binary-ish data leaking into a text field; a stray ESC (U+001B) from ANSI-stripped output.","solutions":["Strip or replace control characters (except \\t \\n \\r) before setting the value.","If the field is a Word body-text path that supports soft breaks, ensure the caller passes allowSoftBreakChar=true and uses \\v intentionally for <w:br/>.","Sanitize upstream (e.g. regex replace [\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F] with '' or a space)."],"exampleFix":"// before\ntext=\"line1\\x00line2\"\n// after\ntext=\"line1line2\"   // control char stripped","handlingStrategy":"validation","validationCode":"// Strip XML-illegal C0 control chars (keep \\t \\n \\r; allow \\v only if caller will convert it):\nstatic string SanitizeForXml(string s, bool keepVerticalTab = false)\n    => System.Text.RegularExpressions.Regex.Replace(s,\n        keepVerticalTab ? @\"[\\x00-\\x08\\x0C\\x0E-\\x1F]\" : @\"[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]\",\n        string.Empty);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Sanitize text from terminals/databases to remove C0 control bytes.","Only pass allowSoftBreakChar=true on paths that convert \\v to a break element.","Validate before setting so save-time data-loss errors never occur."],"tags":["xml","input-validation","control-chars","ooxml","officecli"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}