dotnet/wpf · error · ArgumentOutOfRangeException

SR.CountOfBitsGreatThanRemainingBits

Error message

SR.CountOfBitsGreatThanRemainingBits

What it means

After validating the bit width, BitStream.ReadByte also checks that the requested countOfBits does not exceed the bits remaining in the buffer (_bufferLengthInBits); exceeding it throws ArgumentOutOfRangeException(SR.CountOfBitsGreatThanRemainingBits). It prevents partial reads past the end of the stream.

Solutions

  1. Check remaining bits before each call: only read min(requested, remaining) bits, or stop when fewer remain.
  2. Check BitStream.EndOfStream and track consumed bits to compute the remaining count before reading.
  3. Catch ArgumentOutOfRangeException around decode and treat the ISF stream as truncated.
  4. Align decode logic to byte boundaries so the last partial byte is handled explicitly.

Example fix

// before
byte v = bitStream.ReadByte(8); // may exceed remaining bits
// after
if (bitStream.EndOfStream || bitStream.RemainingBits < 8)
    throw new InvalidDataException("Not enough bits remaining for an 8-bit read");
byte v = bitStream.ReadByte(8);
Defensive patterns

Strategy: validation

Validate before calling

// only issue the read when enough bits remain
if (bitStream.EndOfStream)
    throw new InvalidDataException("No bits remaining");
byte v = bitStream.ReadByte(Math.Min(requestedBits, 8));

Type guard

static bool HasBits(BitStream s, int n) => !s.EndOfStream && s.RemainingBits >= n;

Try / catch

try
{
    byte v = bitStream.ReadByte(8);
}
catch (ArgumentOutOfRangeException)
{
    // fewer bits remained than requested: handle partial/truncated tail
    throw new InvalidDataException("Truncated ISF tail: not enough bits for field");
}

Prevention

When it happens

Trigger: Calling ReadByte(countOfBits) with 1-8 bits when fewer than countOfBits bits remain in the stream (e.g. 4 bits left, requesting 8). Triggered from GetDataFromReader, Uncompress, LoadPackets, and the indexer on truncated data.

Common situations: Reading fixed-width fields (e.g. always 8 bits) from an ISF payload whose final byte is partial; a decode loop that does not account for sub-byte leftovers.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/72c59f7f2a303405. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/Ink/BitStream.cs:241

        /// will be created with the least significant bits set to the 2 unpacked bits
        /// from the stream</remarks>
        internal byte ReadByte(int countOfBits)
        {
            // if the end of the stream has been reached, then throw an exception
            if (EndOfStream)
            {
                throw new System.IO.EndOfStreamException(SR.EndOfStreamReached);
            }

            // we only support 1-8 bits currently, not multiple bytes, and not 0 bits
            if (countOfBits > Native.BitsPerByte || countOfBits <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(countOfBits), countOfBits, SR.CountOfBitsOutOfRange);
            }

            if (countOfBits > _bufferLengthInBits)
            {
                throw new ArgumentOutOfRangeException(nameof(countOfBits), countOfBits, SR.CountOfBitsGreatThanRemainingBits);
            }

            _bufferLengthInBits -= (uint)countOfBits;

            // initialize return byte to 0 before reading from the cache
            byte returnByte = 0;

            // if the partial bit cache contains more bits than requested, then read the
            //      cache only
            if (_cbitsInPartialByte >= countOfBits)
            {
                // retrieve the requested count of most significant bits from the cache
                //      and store them in the least significant positions in the return byte
                int rightShiftPartialByteBy = Native.BitsPerByte - countOfBits;
                returnByte = (byte)(_partialByte >> rightShiftPartialByteBy);

                // reposition any unused portion of the cache in the most significant part of the bit cache
                unchecked // disable overflow checking since we are intentionally throwing away

View on GitHub (pinned to 81131a70a4)