Skip to content

[Breaking change]: Update MemoryStream max capacity #49545

Closed

Description

@jozkee

[Breaking change]: MemoryStream maximum capacity updated and exception behavior changed

Description

Starting in .NET 11 Preview 1, the System.IO.MemoryStream class has been updated to enforce a maximum capacity of 0x7FFFFFC7 bytes, which is the actual maximum length of a byte array supported by the CLR. Previously, MemoryStream allowed capacities up to int.MaxValue (0x7FFFFFFF), which could result in an OutOfMemoryException when attempting to allocate memory beyond the supported limit.

Additionally, the exception behavior has changed when attempting to set a MemoryStream's capacity or length beyond the new maximum. Instead of throwing an OutOfMemoryException, the MemoryStream now throws an ArgumentOutOfRangeException in these scenarios (this is the key difference).

This change ensures that MemoryStream behaves consistently with the CLR's actual memory allocation limits and provides a more accurate exception type for invalid capacity or length values.

Version

.NET 11 Preview 1

Previous behavior

  • MemoryStream allowed capacities up to int.MaxValue (0x7FFFFFFF), which could result in an OutOfMemoryException when attempting to allocate memory beyond the CLR's supported limit of 0x7FFFFFC7.
  • When setting the capacity or length of a MemoryStream to a value greater than the supported limit, an OutOfMemoryException was thrown.

Example:

var stream = new MemoryStream();
stream.SetLength(int.MaxValue); // Throws OutOfMemoryException

New behavior

  • MemoryStream now enforces a maximum capacity of 0x7FFFFFC7 bytes. Attempting to set the capacity or length beyond this limit will throw an ArgumentOutOfRangeException.
  • The exception type for invalid capacity or length values has changed from OutOfMemoryException to ArgumentOutOfRangeException.

Example:

var stream = new MemoryStream();
stream.SetLength(int.MaxValue); // Throws ArgumentOutOfRangeException

Reason for change

This change was introduced to align MemoryStream's behavior with the actual memory allocation limits of the CLR. The previous behavior allowed developers to specify capacities or lengths that exceeded the supported limit, leading to runtime failures with less descriptive exceptions (OutOfMemoryException). By capping the maximum capacity and throwing ArgumentOutOfRangeException, the change improves runtime reliability and provides clearer feedback to developers.

Recommended action

Developers should review any code that sets the capacity or length of a MemoryStream to ensure it does not exceed the new maximum of 0x7FFFFFC7 bytes.

If your code was only catching OutOfMemoryException when working with MemoryStream capacity or length operations, you should update it to also catch ArgumentOutOfRangeException. For example:

var stream = new MemoryStream();
try
{
    stream.SetLength(someLength);
}
catch (OutOfMemoryException)
{
    // Handle out of memory scenario
}
catch (ArgumentOutOfRangeException)
{
    // Handle invalid capacity/length scenario
}

You can also add a check before setting the capacity or length to avoid the exception:

const int MaxMemoryStreamCapacity = Array.MaxLength;

var stream = new MemoryStream();
long newLength = int.MaxValue;

if (newLength > MaxMemoryStreamCapacity)
{
    throw new ArgumentOutOfRangeException(nameof(newLength), $"Length cannot exceed {MaxMemoryStreamCapacity} bytes.");
}

stream.SetLength(newLength);

Affected APIs

  • System.IO.MemoryStream.Capacity
  • System.IO.MemoryStream.SetLength(long)
  • System.IO.MemoryStream.ctor(int)

Category

  • Behavioral change: Existing binaries might behave differently at runtime.

Associated WorkItem - 543464

Activity

  1. jozkee commented on Oct 30, 2025

    @jozkee
    MemberAuthor

    Recommended action

    var stream = new MemoryStream();
    try
    {
        stream.SetLength(someLength);
    }
    catch (OutOfMemoryException)
    {
        // Handle out of memory scenario
    }
    catch (ArgumentOutOfRangeException)
    {
        // Handle invalid capacity/length scenario
    }

    @jkotas is it still possible that an OOMException arises from MemoryStream or can we safely suggest to fully replace it with ArgumentOutOfRangeException?

  2. jkotas commented on Oct 31, 2025

    @jkotas
    Member

    is it still possible that an OOMException arises from MemoryStream

    What would make it impossible? new byte[....] can always throw an OutOfMemory exception if there is not enough memory available on the machine.

    can we safely suggest to fully replace it with ArgumentOutOfRangeException

    It depends on what the code was trying to do.

  3. jkotas commented on Nov 1, 2025

    @jkotas
    Member

    const int MaxMemoryStreamCapacity = 0x7FFFFFC7;

    This is bad suggestion. People should not be hardcoding this constant.

  4. self-assigned this
    on Nov 7, 2025
  5. added
    🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
    on Jan 5, 2026
  6. removed
    🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
    on Jan 5, 2026
  7. added
    🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
    on Jan 5, 2026
  8. removed
    🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
    on Jan 5, 2026
  9. added
    📌 seQUESTeredIdentifies that an issue has been imported into Quest.
    and removed
    🗺️ reQUESTTriggers an issue to be imported into Quest.
    on Jan 6, 2026
  10. gewarren commented on Jan 7, 2026

    @gewarren
    Collaborator

    const int MaxMemoryStreamCapacity = 0x7FFFFFC7;

    This is bad suggestion. People should not be hardcoding this constant.

    @jozkee Should I just exclude this part/suggestion from the recommended actions section?

  11. jozkee commented on Jan 8, 2026

    @jozkee
    MemberAuthor

    @gewarren yes, or recommend Array.MaxValue instead dotnet/runtime#119089 (comment).

  12. moved this from 🔖 Ready to 🏗 In progress in dotnet/docs January 2026 sprinton Jan 8, 2026
  13. moved this from 🏗 In progress to 👀 In review in dotnet/docs January 2026 sprinton Jan 9, 2026
  14. added
    🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
    on Jan 9, 2026
  15. removed
    🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
    on Jan 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

📌 seQUESTeredIdentifies that an issue has been imported into Quest.breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions