XINCGer/Unity3DTraining · error · ArgumentOutOfRangeException

Offset must be within the buffer

Error message

Offset must be within the buffer

What it means

CodedInputStream's byte[]/offset/length constructor validates that the offset lies within the supplied buffer. If offset is negative or greater than buffer.Length, ArgumentOutOfRangeException('Offset must be within the buffer') is thrown before any parsing happens. This is defensive argument validation in Google.Protobuf; the stream is never created.

Solutions

  1. Clamp/validate the offset before constructing: ensure 0 <= offset <= buffer.Length.
  2. Verify the offset computation (e.g. bytes consumed so far) against the actual buffer length actually received.
  3. If the offset can legitimately be at/after the end, guard with 'if (offset >= buffer.Length) skip/return' instead of constructing a reader.
  4. Use a checked bounds helper so offsets are always derived from buffer.Length rather than hard-coded assumptions.

Example fix

// before
var stream = new CodedInputStream(payload, offset, payload.Length - offset);
// after
if (offset < 0 || offset > payload.Length)
    throw new ArgumentException($"Bad offset {offset} for buffer of {payload.Length} bytes");
var stream = new CodedInputStream(payload, offset, payload.Length - offset);
Defensive patterns

Strategy: validation

Validate before calling

if (offset < 0 || offset > buffer.Length) throw new ArgumentException($"offset {offset} out of bounds for buffer length {buffer.Length}", nameof(offset));

Type guard

bool IsValidOffset(byte[] buffer, int offset) => buffer != null && offset >= 0 && offset <= buffer.Length;

Try / catch

try { var s = new CodedInputStream(buffer, offset, length); ... } catch (ArgumentOutOfRangeException ex) { logger.LogError(ex, "Invalid buffer window offset={Offset} len={Length}", offset, length); return null; }

Prevention

When it happens

Trigger: new CodedInputStream(buffer, offset, length) where offset < 0 or offset > buffer.Length. E.g. passing an offset captured from a previous read past the buffer end, or -1 from a failed lookup used directly as the offset.

Common situations: Fragmenting a received socket payload into messages and computing the next message offset incorrectly; reusing a stale offset after the buffer was replaced with a shorter array; passing byte[] objects received from unmanaged/interop code with mismatched lengths.

Related errors


AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12). Data as JSON: /api/errors/711b93927ed9a150. Report an issue: GitHub.

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/CodedInputStream.cs:136

        // Note that the checks are performed such that we don't end up checking obviously-valid things
        // like non-null references for arrays we've just created.

        /// <summary>
        /// Creates a new CodedInputStream reading data from the given byte array.
        /// </summary>
        public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length)
        {
        }

        /// <summary>
        /// Creates a new <see cref="CodedInputStream"/> that reads from the given byte array slice.
        /// </summary>
        public CodedInputStream(byte[] buffer, int offset, int length)
            : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length)
        {
            if (offset < 0 || offset > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
            }
            if (length < 0 || offset + length > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");
            }
        }

        /// <summary>
        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream, which will be disposed
        /// when the returned object is disposed.
        /// </summary>
        /// <param name="input">The stream to read from.</param>
        public CodedInputStream(Stream input) : this(input, false)
        {
        }

        /// <summary>
        /// Creates a new <see cref="CodedInputStream"/> reading data from the given stream.

View on GitHub (pinned to 016f98412e)