XINCGer/Unity3DTraining · error · ArgumentOutOfRangeException

Recursion limit must be positive

Error message

Recursion limit must be positive

What it means

The same internal constructor requires recursionLimit > 0. The recursion limit caps how deeply nested protobuf groups/messages may be while parsing, protecting against stack exhaustion from malicious or deeply nested payloads. Zero or negative values are rejected immediately.

Solutions

  1. Use a sane positive recursion limit (Google.Protobuf defaults to 64); pass Math.Max(1, configuredLimit).
  2. Initialize the configuration field that carries the recursion limit; do not rely on default(int).
  3. Prefer the public CodedInputStream constructors over the internal overload.

Example fix

// before
var reader = NewInternal(input, buf, pos, size, sizeLimit, config.RecursionLimit);
// after
int recursionLimit = config.RecursionLimit > 0 ? config.RecursionLimit : 64;
var reader = NewInternal(input, buf, pos, size, sizeLimit, recursionLimit);
Defensive patterns

Strategy: validation

Validate before calling

int recursionLimit = Math.Max(1, configuredRecursionLimit);

Type guard

bool IsValidRecursionLimit(int limit) => limit > 0;

Try / catch

try { CreateReader(..., recursionLimit); } catch (ArgumentOutOfRangeException ex) { logger.LogError(ex, "recursionLimit must be positive, got {V}", recursionLimit); recursionLimit = 64; }

Prevention

When it happens

Trigger: Internal construction with a recursionLimit of 0 or less — e.g. passing default(int), or copying a limit field from a configuration object that was never initialized.

Common situations: Custom deserialization pipelines (reflection/InternalsVisibleTo) with a config struct where the recursion-depth setting defaulted to 0; copying constants incorrectly when adapting the library.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

        /// <summary>
        /// Creates a new CodedInputStream reading data from the given
        /// stream and buffer, using the specified limits.
        /// </summary>
        /// <remarks>
        /// This chains to the version with the default limits instead of vice versa to avoid
        /// having to check that the default values are valid every time.
        /// </remarks>
        internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit)
            : this(input, buffer, bufferPos, bufferSize)
        {
            if (sizeLimit <= 0)
            {
                throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive");
            }
            if (recursionLimit <= 0)
            {
                throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive");
            }
            this.sizeLimit = sizeLimit;
            this.recursionLimit = recursionLimit;
        }
        #endregion

        /// <summary>
        /// Creates a <see cref="CodedInputStream"/> with the specified size and recursion limits, reading
        /// from an input stream.
        /// </summary>
        /// <remarks>
        /// This method exists separately from the constructor to reduce the number of constructor overloads.
        /// It is likely to be used considerably less frequently than the constructors, as the default limits
        /// are suitable for most use cases.
        /// </remarks>
        /// <param name="input">The input stream to read from</param>
        /// <param name="sizeLimit">The total limit of data to read from the stream.</param>
        /// <param name="recursionLimit">The maximum recursion depth to allow while reading.</param>

View on GitHub (pinned to 016f98412e)