{"record":{"id":"51829cfedf7e1f3e","repo":"temporalio/temporal","slug":"interval-is-not-a-valid-duration-w","errorCode":null,"errorMessage":"interval is not a valid duration: %w","messagePattern":"interval is not a valid duration: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"service/frontend/workflow_handler.go","lineNumber":7016,"sourceCode":"\tsizeLimitWarn := wh.config.BlobSizeLimitWarn(namespaceName)\n\treturn common.CheckEventBlobSizeLimit(\n\t\tpayloadSize,\n\t\tsizeLimitWarn,\n\t\tsizeLimitError,\n\t\tnamespaceName,\n\t\tscheduleID,\n\t\t\"\",\n\t\twh.metricsScope(ctx).WithTags(metrics.CommandTypeTag(enumspb.COMMAND_TYPE_UNSPECIFIED.String())),\n\t\twh.throttledLogger,\n\t\toperation,\n\t)\n}\n\nfunc validateScheduleIntervalDurations(spec *schedulepb.ScheduleSpec) error {\n\tfor _, interval := range spec.GetInterval() {\n\t\tif d := interval.GetInterval(); d != nil {\n\t\t\tif err := d.CheckValid(); err != nil {\n\t\t\t\treturn fmt.Errorf(\"interval is not a valid duration: %w\", err)\n\t\t\t}\n\t\t}\n\t\tif d := interval.GetPhase(); d != nil {\n\t\t\tif err := d.CheckValid(); err != nil {\n\t\t\t\treturn fmt.Errorf(\"phase is not a valid duration: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateScheduleTimestamps(spec *schedulepb.ScheduleSpec) error {\n\tif err := validateTimestamp(spec.GetStartTime(), \"start time\"); err != nil {\n\t\treturn err\n\t}\n\treturn validateTimestamp(spec.GetEndTime(), \"end time\")\n}\n","sourceCodeStart":6998,"sourceCodeEnd":7034,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/service/frontend/workflow_handler.go#L6998-L7034","documentation":"Returned when an interval spec's `interval` duration in a ScheduleSpec fails protobuf timestamp/duration CheckValid() (e.g. nil semantics, negative, or malformed wire data). Temporal validates all interval spec fields server-side before accepting schedule creation/update to prevent undefined scheduler behavior.","triggerScenarios":"Calling CreateScheduleAsync or UpdateScheduleAsync (via ValidateScheduleSpec → validateScheduleIntervalDurations) with a ScheduleSpec whose Interval[*].Interval is a zero-value or invalid durationpb.Duration (seconds/nanos inconsistent, negative).","commonSituations":"Constructing durationpb.Duration manually with wrong sign; decoding corrupted protobuf; deserializing specs from config where seconds/nanos were miscomputed; SDK users passing nil-checked but semantically invalid durations.","solutions":["Ensure each interval duration is > 0 and has consistent seconds/nanoseconds (0 <= nanos < 1e9)","Use durationpb.New(time.Duration) instead of hand-building durationpb.Duration","Check for negative or overflowed values when converting from config strings","Use an SDK typed duration type (e.g. Go time.Duration) so the client serializes correctly"],"exampleFix":"// before\ninterval := &schedulepb.IntervalSpec{Interval: &durationpb.Duration{Seconds: -3600}}\n// after\ninterval := &schedulepb.IntervalSpec{Interval: durationpb.New(1 * time.Hour)}","handlingStrategy":"validation","validationCode":"func validInterval(d *durationpb.Duration) bool { return d != nil && d.CheckValid() == nil && d.AsDuration() > 0 }","typeGuard":"func isDurationpb(v interface{}) *durationpb.Duration { d, ok := v.(*durationpb.Duration); return d }","tryCatchPattern":"err := validateScheduleIntervalDurations(spec); if err != nil { return serviceerror.NewInvalidArgumentf(...) }","preventionTips":["Always build durations with durationpb.New from time.Duration","Never hand-set Seconds/Nanos fields","Validate spec client-side before Create/UpdateSchedule"],"tags":["duration","protobuf","schedule","validation","go"],"backgroundTag":"invalid-duration","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}