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
- Use a sane positive recursion limit (Google.Protobuf defaults to 64); pass Math.Max(1, configuredLimit).
- Initialize the configuration field that carries the recursion limit; do not rely on default(int).
- 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
- Use Google.Protobuf's default recursion limit (64) unless you have a measured reason otherwise.
- Give config fields non-zero defaults; treat 0 as 'unset' and substitute the default.
- Never pass default(int) as a limit parameter.
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
- Offset must be within the buffer
- Length must be non-negative and within the buffer
- Size limit must be positive
- SkipLastField cannot be called at the end of a stream
- SkipLastField called on an end-group tag, indicating that…
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)