iOfficeAI/OfficeCLI · error · ArgumentException

{propName} contains XML-illegal control character U+{(int)c:

Error message

{propName} contains XML-illegal control character U+{(int)c:X4} at position {i}. Allowed control chars: \t, \n, \r{allowSoftBreakChar ? ", \v." : "."}

What it means

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.

Source

Thrown at src/officecli/Core/ParseHelpers.cs:851

    /// (U+D800–U+DFFF without a matching pair), and the U+FFFE / U+FFFF
    /// noncharacters.
    /// </summary>
    public static void ValidateXmlText(string? value, string propName, bool allowSoftBreakChar = false)
    {
        if (value == null) return;
        for (int i = 0; i < value.Length; i++)
        {
            char c = value[i];
            if (c == '\t' || c == '\n' || c == '\r') continue;
            // '\v' (0x0B) is XML-illegal as character data. It is allowed ONLY
            // when the caller consumes it into a break ELEMENT before
            // serialization (NEWLINE-SEMANTICS-V2: AppendTextWithBreaks turns
            // '\v' into <w:br/>). Callers that write validated text verbatim
            // into XML (chart titles, xlsx cell values, headers, ...) keep the
            // strict default so '\v' can never reach raw character data.
            if (c == '\v' && allowSoftBreakChar) continue;
            if (c < 0x20)
                throw new ArgumentException(
                    $"{propName} contains XML-illegal control character U+{(int)c:X4} at position {i}. " +
                    "Allowed control chars: \\t, \\n, \\r" +
                    (allowSoftBreakChar ? ", \\v." : "."));
            // UTF-16 surrogates only valid in pairs (high then low). A lone
            // half is illegal in XML 1.0 character data.
            if (char.IsHighSurrogate(c))
            {
                if (i + 1 >= value.Length || !char.IsLowSurrogate(value[i + 1]))
                    throw new ArgumentException(
                        $"{propName} contains an unpaired high surrogate U+{(int)c:X4} at position {i}. Use a complete UTF-16 surrogate pair.");
                i++; // skip the matched low surrogate
                continue;
            }
            if (char.IsLowSurrogate(c))
                throw new ArgumentException(
                    $"{propName} contains an unpaired low surrogate U+{(int)c:X4} at position {i}. Use a complete UTF-16 surrogate pair.");
            if (c == 0xFFFE || c == 0xFFFF)
                throw new ArgumentException(

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Strip or replace control characters (except \t \n \r) before setting the value.
  2. 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/>.
  3. Sanitize upstream (e.g. regex replace [\x00-\x08\x0B\x0C\x0E-\x1F] with '' or a space).

Example fix

// before
text="line1\x00line2"
// after
text="line1line2"   // control char stripped
Defensive patterns

Strategy: validation

Validate before calling

// Strip XML-illegal C0 control chars (keep \t \n \r; allow \v only if caller will convert it):
static string SanitizeForXml(string s, bool keepVerticalTab = false)
    => System.Text.RegularExpressions.Regex.Replace(s,
        keepVerticalTab ? @"[\x00-\x08\x0C\x0E-\x1F]" : @"[\x00-\x08\x0B\x0C\x0E-\x1F]",
        string.Empty);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/d8399ab564126582. Report an issue: GitHub.