XINCGer/Unity3DTraining · error · InvalidProtocolBufferException
Invalid Duration value
Error message
Invalid Duration value: {token.StringValue} What it means
After the token is confirmed a string, MergeDuration applies DurationRegex; strings that do not match the canonical Duration JSON grammar (sign, integer seconds, optional fractional part, mandatory 's' suffix) throw 'Invalid Duration value: ...'.
Solutions
- Format the duration as seconds with optional fraction and trailing 's': "5s", "-1.5s", "0.000000001s".
- Convert TimeSpan with $"{(long)ts.TotalSeconds}.{ts.Milliseconds...}s" or similar canonical form.
- Catch InvalidProtocolBufferException and normalize/repair the string before retrying.
- Validate with regex ^-?\d+(\.\d{1,9})?s$ before calling the parser.
Example fix
// before
var d = parser.Parse<Duration>("\"PT5S\"");
// after
var d = parser.Parse<Duration>("\"5s\""); Defensive patterns
Strategy: validation
Validate before calling
static readonly System.Text.RegularExpressions.Regex DurationRegex =
new(@"^-?\d+(\.\d{1,9})?s$");
bool IsCanonicalDuration(string s) => DurationRegex.IsMatch(s); Type guard
bool IsValidDurationString(string s) => DurationRegex.IsMatch(s);
Try / catch
try { var d = JsonParser.Default.Parse<Duration>(jsonValue); }
catch (InvalidProtocolBufferException ex) when (ex.Message.StartsWith("Invalid Duration"))
{ log.Warn($"Bad duration '{raw}'"); throw new ArgumentException("duration must look like \"3.5s\"", ex); } Prevention
- Always include the trailing 's'
- Don't use ISO 8601 (PT5S) or TimeSpan.ToString() output directly
- Convert numeric seconds with $"{seconds}s"
- Pre-validate with a regex before parsing
When it happens
Trigger: Parsing values like "5" (no s), "5 sec", "PT5S" (ISO 8601), "1h30m", "", or ".5s" via JsonParser for a Duration field — anything not matching ^-?[0-9]+(\.[0-9]{1,9})?s$.
Common situations: Using ISO 8601 durations (PT5S) from other systems; omitting the trailing 's'; passing TimeSpan.ToString() output that lacks the 's'; locale-formatted numbers with commas.
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
Related errors
- Expected string value for Duration
- Expected end of JSON after object
- Expected an object
- Unexpected token type
- Multiple values specified for oneof
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/b9127eb7bdc923b7.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/JsonParser.cs:868
message.Descriptor.Fields[Timestamp.SecondsFieldNumber].Accessor.SetValue(message, timestamp.Seconds);
message.Descriptor.Fields[Timestamp.NanosFieldNumber].Accessor.SetValue(message, timestamp.Nanos);
}
catch (FormatException)
{
throw new InvalidProtocolBufferException("Invalid Timestamp value: " + token.StringValue);
}
}
private static void MergeDuration(IMessage message, JsonToken token)
{
if (token.Type != JsonToken.TokenType.StringValue)
{
throw new InvalidProtocolBufferException("Expected string value for Duration");
}
var match = DurationRegex.Match(token.StringValue);
if (!match.Success)
{
throw new InvalidProtocolBufferException("Invalid Duration value: " + token.StringValue);
}
var sign = match.Groups["sign"].Value;
var secondsText = match.Groups["int"].Value;
// Prohibit leading insignficant zeroes
if (secondsText[0] == '0' && secondsText.Length > 1)
{
throw new InvalidProtocolBufferException("Invalid Duration value: " + token.StringValue);
}
var subseconds = match.Groups["subseconds"].Value;
var multiplier = sign == "-" ? -1 : 1;
try
{
long seconds = long.Parse(secondsText, CultureInfo.InvariantCulture) * multiplier;
int nanos = 0;
if (subseconds != "")
{
// This should always work, as we've got 1-9 digits.View on GitHub (pinned to 016f98412e)