{"record":{"id":"c10f42037299dda8","repo":"XINCGer/Unity3DTraining","slug":"duration-was-not-a-valid-normalized-duration","errorCode":null,"errorMessage":"Duration was not a valid normalized duration","messagePattern":"Duration was not a valid normalized duration","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/WellKnownTypes/DurationPartial.cs","lineNumber":91,"sourceCode":"            // We only have a problem is one is strictly negative and the other is\n            // strictly positive.\n            return Math.Sign(seconds) * Math.Sign(nanoseconds) != -1;\n        }\n\n        /// <summary>\n        /// Converts this <see cref=\"Duration\"/> to a <see cref=\"TimeSpan\"/>.\n        /// </summary>\n        /// <remarks>If the duration is not a precise number of ticks, it is truncated towards 0.</remarks>\n        /// <returns>The value of this duration, as a <c>TimeSpan</c>.</returns>\n        /// <exception cref=\"InvalidOperationException\">This value isn't a valid normalized duration, as\n        /// described in the documentation.</exception>\n        public TimeSpan ToTimeSpan()\n        {\n            checked\n            {\n                if (!IsNormalized(Seconds, Nanos))\n                {\n                    throw new InvalidOperationException(\"Duration was not a valid normalized duration\");\n                }\n                long ticks = Seconds * TimeSpan.TicksPerSecond + Nanos / NanosecondsPerTick;\n                return TimeSpan.FromTicks(ticks);\n            }\n        }\n\n        /// <summary>\n        /// Converts the given <see cref=\"TimeSpan\"/> to a <see cref=\"Duration\"/>.\n        /// </summary>\n        /// <param name=\"timeSpan\">The <c>TimeSpan</c> to convert.</param>\n        /// <returns>The value of the given <c>TimeSpan</c>, as a <c>Duration</c>.</returns>\n        public static Duration FromTimeSpan(TimeSpan timeSpan)\n        {\n            checked\n            {\n                long ticks = timeSpan.Ticks;\n                long seconds = ticks / TimeSpan.TicksPerSecond;\n                int nanos = (int) (ticks % TimeSpan.TicksPerSecond) * NanosecondsPerTick;","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/XINCGer/Unity3DTraining/blob/016f98412ea5e11756b286736f479b66ab6216d5/NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/WellKnownTypes/DurationPartial.cs#L73-L109","documentation":"Duration.ToTimeSpan converts the duration only if it is normalized: nanos within [-999999999, 999999999] and seconds/nanos sign-consistent, within valid ranges. If IsNormalized fails it throws InvalidOperationException. Google.Protobuf deliberately rejects non-normalized values (e.g. 1s + 1_500_000_000ns) rather than guessing an interpretation.","triggerScenarios":"Calling duration.ToTimeSpan() on a Duration whose Nanos are out of the -999999999..999999999 range, whose Seconds/Nanos signs disagree, or which was deserialized from a producer that emitted unnormalized durations (including negative values outside bounds).","commonSituations":"Receiving durations from non-C# services or hand-rolled protobuf encoders that don't normalize; computing durations by adding nanos without carrying into seconds.","solutions":["Normalize before converting: fold Nanos into Seconds (carry 1_000_000_000 ns into 1 second, adjust signs) then call ToTimeSpan().","Fix the producer to emit normalized durations per the proto spec (canonical form).","Or use Duration.FromSeconds/Nanos helpers on the C# side to build durations instead of hand-assembling them."],"exampleFix":"// before\nvar ts = duration.ToTimeSpan(); // throws when nanos unnormalized\n// after\nlong totalNanos = duration.Seconds * 1_000_000_000L + duration.Nanos;\nvar normalized = Duration.FromTimeSpan(TimeSpan.FromTicks(totalNanos / 100));\nvar ts = normalized.ToTimeSpan();","handlingStrategy":"try-catch","validationCode":"bool IsNormalizedDuration(Duration d) =>\n    d.Nanos >= -999999999 && d.Nanos <= 999999999 &&\n    (d.Seconds > 0 || d.Nanos >= 0) && (d.Seconds < 0 || d.Nanos <= 0);","typeGuard":"bool IsValidDuration(Duration d) => d.Nanos >= -999999999 && d.Nanos <= 999999999 && Math.Sign(d.Seconds == 0 ? d.Nanos : d.Seconds) == Math.Sign(d.Nanos == 0 ? d.Seconds : d.Nanos);","tryCatchPattern":"try { return duration.ToTimeSpan(); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"normalized\")) { long n = duration.Seconds * 1_000_000_000L + duration.Nanos; return TimeSpan.FromTicks(n / 100); }","preventionTips":["Normalize durations at ingestion: carry nanos beyond ±999,999,999 into seconds.","Build Duration values with Duration.FromSeconds/FromTimeSpan instead of setting fields manually.","Validate producer output for spec-compliant (canonical) durations in contract tests."],"tags":["protobuf","well-known-types","duration","invalid-state"],"backgroundTag":"invalid-duration-format","analyzedSha":"016f98412ea5e11756b286736f479b66ab6216d5","analyzedAt":"2026-09-12T01:08:58.711Z","contentChangedAt":"2026-09-12T01:08:58.711Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}