dotnet/machinelearning · error · ArgumentOutOfRangeException

Offset must be non-negative. (Parameter 'offset')

Error message

Offset must be non-negative. (Parameter 'offset')

What it means

ValidateBufferArguments throws ArgumentOutOfRangeException for a negative offset. The (buffer, offset, count) triple must describe a valid region inside the buffer, and a negative offset cannot be, so the call is rejected before any I/O.

Solutions

  1. Clamp or validate offset >= 0 before the call
  2. Fix the computation that produces the offset (e.g. use Math.Max(0, value))
  3. Reorder logic so the offset is derived from bytes actually read

Example fix

// before
int offset = position - headerSize; // can go negative
StreamUtils.ReadExactly(stream, buffer, offset, count);
// after
int offset = Math.Max(0, position - headerSize);
StreamUtils.ReadExactly(stream, buffer, offset, count);
Defensive patterns

Strategy: validation

Validate before calling

if (offset < 0) throw new ArgumentException("Offset must be non-negative", nameof(offset));
StreamUtils.ReadExactly(stream, buffer, offset, count);

Try / catch

try { StreamUtils.ReadExactly(stream, buffer, offset, count); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "offset")
{ /* fix offset computation */ }

Prevention

When it happens

Trigger: Calling ReadExactly (or another ValidateBufferArguments-backed helper) with a negative offset, typically from an unvalidated computed offset (e.g. position - size going below zero).

Common situations: Accumulating offsets in loops that underflow; passing a negative variable after subtracting a header size; mixing up offset and position parameters.

Related errors


AI-assisted analysis of dotnet/machinelearning@7b76e69cf9 (2026-09-11). Data as JSON: /api/errors/7a36515bc1432602. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.ML.Data/Utilities/StreamUtils.cs:201

        /// <param name="offset">The integer "offset" argument passed to the reading or writing method.</param>
        /// <param name="count">The integer "count" argument passed to the reading or writing method.</param>
        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> was null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <paramref name="offset"/> was outside the bounds of <paramref name="buffer"/>, or
        /// <paramref name="count"/> was negative, or the range specified by the combination of
        /// <paramref name="offset"/> and <paramref name="count"/> exceed the length of <paramref name="buffer"/>.
        /// </exception>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void ValidateBufferArguments(byte[] buffer, int offset, int count)
        {
            if (buffer is null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(offset), "Offset must be non-negative.");
            }

            if ((uint)count > buffer.Length - offset)
            {
                throw new ArgumentOutOfRangeException(nameof(count), "Count must be non-negative and count must not exceed buffer.Length - offset.");
            }
        }

        // No argument checking is done here. It is up to the caller.
        private static int ReadAtLeastCore(Stream stream, byte[] buffer, int offset, int minimumBytes, bool throwOnEndOfStream)
        {
            Debug.Assert(minimumBytes <= buffer.Length);
            int count = minimumBytes;
            int totalRead = 0;
            while (totalRead < minimumBytes)
            {
                int read = stream.Read(buffer, offset, count);
                offset += read;

View on GitHub (pinned to 7b76e69cf9)