Libs: (bug) Valid custom numeric format string is rejected with FormatException when a literal digit run after a leading letter exceeds 9 digits

Open Beginner friendly
#134,102 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp
Domain
backend

Research direction

Start in src/libraries/Common/src/System/Number.Formatting.Common.cs at Number.ParseFormatSpecifier and trace the digit-scanning path used by numeric ToString and TryFormat. Add regression coverage for long digit runs followed by a non-digit, confirming those custom formats work while a genuinely standard format with excessive precision still throws FormatException.

Written by the indexing model from the issue text.

Description

area-System.Runtime untriaged

Number.ParseFormatSpecifier throws FormatException while scanning the precision digit run of a possible standard format, before it can see that the string is actually a custom format. So "A1111111111-0" is rejected while the otherwise identical "A111111111-0" (one digit fewer) formats fine.

Minimal Repro
using System;
using System.Globalization;

internal static class P
{
    private static void Show(string fmt)
    {
        try { Console.WriteLine($"\"{fmt}\" => \"{7.ToString(fmt, CultureInfo.InvariantCulture)}\""); }
        catch (Exception e) { Console.WriteLine($"\"{fmt}\" => {e.GetType().Name}"); }
    }

    private static void Main()
    {
        Show("A111111111-0");   // 9-digit literal run  -> custom format, works
        Show("A1111111111-0");  // 10-digit literal run -> same shape, throws
        Show("A123456780Z");    // works
        Show("A1234567890Z");   // throws
        Show("A1234567890");    // genuinely standard-shaped: FormatException is correct here
    }
}
Expected

A format string that is not <letter><optional precision> is a custom format string, so the trailing -0 / Z makes these custom and they must format:

"A111111111-0"  => "A111111111-7"
"A1111111111-0" => "A1111111111-7"
"A123456780Z"   => "A123456787Z"
"A1234567890Z"  => "A1234567897Z"
"A1234567890"   => FormatException     (correct: standard format, precision > 999,999,999)
Actual
"A111111111-0" => "A111111111-7"
"A1111111111-0" => FormatException
"A123456780Z" => "A123456787Z"
"A1234567890Z" => FormatException
"A1234567890" => FormatException
Notes

Root cause: in src/libraries/Common/src/System/Number.Formatting.Common.cs, the digit-scanning loop calls ThrowHelper.ThrowFormatException_BadFormatSpecifier() as soon as n >= 100_000_000, instead of remembering the overflow and falling through to the custom-format path. The throw is only correct when the scan actually ends at the end of the format (or at \0) — the condition the following if already tests.

Trigger: leading ASCII letter, a digit run reaching 100,000,000, followed by at least one non-digit. Affects all numeric types routed through this helper (int, long, float, double, Half, decimal, BigInteger, …) and both ToString and TryFormat. Also repros on .NET 10.0.7 and .NET 8.0.26; likely introduced by #72647 (.NET 7), which replaced the previous int-overflow-only check. No test covers a digit run followed by a non-digit.

Dominant language
C#
Stars
18.3k
Forks
5.6k
Avg merge
2d 20h
Merged PRs (30d)
633

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/runtime

All issues in dotnet/runtime

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.