XINCGer/Unity3DTraining · error · InvalidProtocolBufferException

Invalid numeric value

Error message

Invalid numeric value: {text}

What it means

When a JSON string is parsed into a float/double field, ParseSingleStringValue checks whether the parsed value ended up as Infinity, -Infinity or NaN. The protobuf JSON mapping only permits the exact tokens 'Infinity', '-Infinity' and 'NaN' to denote these values; if the CLR parser produced a special value from some other text (a side effect of lenient parsing, e.g. case variants or whitespace handling), ValidateInfinityAndNan throws InvalidProtocolBufferException('Invalid numeric value: ' + text).

Solutions

  1. Use the exact tokens 'Infinity', '-Infinity' or 'NaN' as unquoted-per-spec JSON strings for special float values
  2. Normalize incoming tokens (trim, fix case, strip '+') before parsing
  3. Replace non-finite values with valid finite numbers or omit the field if special values are not required
  4. Validate float tokens against the three allowed special strings before calling the parser

Example fix

// before
parser.Parse<Point>("{\"x\": \"infinity\"}"); // wrong spelling
// after
parser.Parse<Point>("{\"x\": \"Infinity\"}");
Defensive patterns

Strategy: validation

Validate before calling

bool IsValidSpecialFloat(string text) =>
    text == "Infinity" || text == "-Infinity" || text == "NaN" ||
    double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) && !double.IsInfinity(d) && !double.IsNaN(d);

Try / catch

try { return JsonParser.Default.Parse<T>(json); }
catch (InvalidProtocolBufferException ex) when (ex.Message.StartsWith("Invalid numeric value")) {
    logger.LogWarning(ex, "Non-spec float token (Infinity/NaN spelling)");
    return null;
}

Prevention

When it happens

Trigger: JsonParser.Parse<T> on float/double fields with strings like 'infinity', '+Infinity', ' NaN ', or 'Inf' — tokens the CLR parser may accept leniently but that are not the spec-mandated spellings.

Common situations: JSON generated by non-.NET serializers using different infinity spellings; data round-tripped through logs or metrics systems that lowercase tokens; hand-written JSON with sign-prefixed Infinity.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/JsonParser.cs:779

            }
            catch (OverflowException)
            {
                throw new InvalidProtocolBufferException("Value out of range: " + text);
            }
        }

        /// <summary>
        /// Checks that any infinite/NaN values originated from the correct text.
        /// This corrects the lenient whitespace handling of double.Parse/float.Parse, as well as the
        /// way that Mono parses out-of-range values as infinity.
        /// </summary>
        private static void ValidateInfinityAndNan(string text, bool isPositiveInfinity, bool isNegativeInfinity, bool isNaN)
        {
            if ((isPositiveInfinity && text != "Infinity") ||
                (isNegativeInfinity && text != "-Infinity") ||
                (isNaN && text != "NaN"))
            {
                throw new InvalidProtocolBufferException("Invalid numeric value: " + text);
            }
        }

        private static void MergeTimestamp(IMessage message, JsonToken token)
        {
            if (token.Type != JsonToken.TokenType.StringValue)
            {
                throw new InvalidProtocolBufferException("Expected string value for Timestamp");
            }
            var match = TimestampRegex.Match(token.StringValue);
            if (!match.Success)
            {
                throw new InvalidProtocolBufferException("Invalid Timestamp value: " + token.StringValue);
            }
            var dateTime = match.Groups["datetime"].Value;
            var subseconds = match.Groups["subseconds"].Value;
            var offset = match.Groups["offset"].Value;

View on GitHub (pinned to 016f98412e)