iOfficeAI/OfficeCLI · error · ArgumentException

number format has unbalanced square brackets: '{formatCode}'

Error message

number format has unbalanced square brackets: '{formatCode}'. Bracket codes ([Red], [>=100], [h]) must be closed; writing an unbalanced bracket makes Excel refuse to open the file.

What it means

Thrown when a custom Excel number format has unbalanced square brackets. Bracket codes ([Red], [>=100], [h]) must be closed; the writer counts brackets outside quoted literals (and treats escaped \[ as a literal), then rejects the format if the depth is non-zero. Real Excel refuses the whole file (0x800A03EC) on an unbalanced bracket, so this guard blocks it at write time.

Source

Thrown at src/officecli/Core/ExcelStyleManager.cs:742

        // Unbalanced [brackets] (e.g. formatCode="[") pass schema validation
        // but real Excel refuses the whole file (0x800A03EC). Count outside
        // quoted literals; escaped \[ is a literal char.
        int nfBracketDepth = 0;
        bool nfBrQuote = false;
        for (int i = 0; i < formatCode.Length; i++)
        {
            var c = formatCode[i];
            if (c == '"') nfBrQuote = !nfBrQuote;
            else if (!nfBrQuote && c == '\\') i++;
            else if (!nfBrQuote && c == '[') nfBracketDepth++;
            else if (!nfBrQuote && c == ']')
            {
                nfBracketDepth--;
                if (nfBracketDepth < 0) break;
            }
        }
        if (nfBracketDepth != 0)
            throw new ArgumentException(
                $"number format has unbalanced square brackets: '{formatCode}'. Bracket codes ([Red], [>=100], [h]) must be closed; writing an unbalanced bracket makes Excel refuse to open the file.");

        // Unquoted letters outside Excel's token alphabet (date/time/era/
        // General letters) make real Excel refuse the whole file
        // (0x800A03EC) while schema validation stays green — e.g. a typoed
        // numfmt=invalid_fmt instead of "invalid_fmt"0. Excel's grammar is
        // quirky enough (abc opens, xyz does not) that a hard reject risks
        // false positives on locale codes, so warn instead of block.
        {
            const string TokenLetters = "abcdeghlmnprsty";
            bool warnQuote = false;
            int warnBracket = 0;
            char? suspect = null;
            for (int i = 0; i < formatCode.Length && suspect == null; i++)
            {
                var c = formatCode[i];
                if (c == '"') { warnQuote = !warnQuote; continue; }
                if (warnQuote) continue;

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Balance the brackets: ensure every '[' has a matching ']' in the format code.
  2. If you need a literal bracket in output, escape it as \[ or \] or put it inside a quoted literal "[".
  3. Validate the format with the project's number-format checker before writing.

Example fix

// before
style.NumberFormat = "[Red0.00"; // missing ]
// after
style.NumberFormat = "[Red]0.00";
Defensive patterns

Strategy: validation

Validate before calling

static bool BracketsBalanced(string fmt)
{
    int depth = 0; bool inQuote = false;
    for (int i = 0; i < fmt.Length; i++)
    {
        var c = fmt[i];
        if (c == '"') inQuote = !inQuote;
        else if (!inQuote && c == '\\') i++;
        else if (!inQuote && c == '[') depth++;
        else if (!inQuote && c == ']') { depth--; if (depth < 0) return false; }
    }
    return depth == 0;
}

Try / catch

try { ApplyNumberFormat(fmt); }
catch (ArgumentException ex) when (ex.Message.Contains("unbalanced square brackets"))
{ /* fix or escape brackets */ }

Prevention

When it happens

Trigger: Setting numberFormat to a string with an unclosed bracket such as "[Red" or "[>=100" or a stray "["; the validator counts '[' and ']' ignoring those inside double quotes or after a backslash and throws when depth != 0.

Common situations: Hand-building a format string with string interpolation that drops the closing ']'; a typoed predicate like [>100 instead of [>100]; copy-paste truncating the format.

Related errors


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