{"record":{"id":"5e27dda31618a115","repo":"go-kratos/kratos","slug":"s-seconds-out-of-range-v","errorCode":null,"errorMessage":"%s: seconds out of range %v","messagePattern":"(.+?): seconds out of range (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"encoding/form/well_known_types.go","lineNumber":48,"sourceCode":"\n\t// google.protobuf.Struct.\n\tstructMessageFullname   protoreflect.FullName    = \"google.protobuf.Struct\"\n\tstructFieldsFieldNumber protoreflect.FieldNumber = 1\n\n\tfieldMaskFullName protoreflect.FullName = \"google.protobuf.FieldMask\"\n)\n\nfunc marshalTimestamp(m protoreflect.Message) (string, error) {\n\tfds := m.Descriptor().Fields()\n\tfdSeconds := fds.ByNumber(timestampSecondsFieldNumber)\n\tfdNanos := fds.ByNumber(timestampNanosFieldNumber)\n\n\tsecsVal := m.Get(fdSeconds)\n\tnanosVal := m.Get(fdNanos)\n\tsecs := secsVal.Int()\n\tnanos := nanosVal.Int()\n\tif secs < minTimestampSeconds || secs > maxTimestampSeconds {\n\t\treturn \"\", fmt.Errorf(\"%s: seconds out of range %v\", timestampMessageFullname, secs)\n\t}\n\tif nanos < 0 || nanos > secondsInNanos {\n\t\treturn \"\", fmt.Errorf(\"%s: nanos out of range %v\", timestampMessageFullname, nanos)\n\t}\n\t// Uses RFC 3339, where generated output will be Z-normalized and uses 0, 3,\n\t// 6 or 9 fractional digits.\n\tt := time.Unix(secs, nanos).Local()\n\tx := t.Format(\"2006-01-02T15:04:05.000000000\")\n\tx = strings.TrimSuffix(x, \"000\")\n\tx = strings.TrimSuffix(x, \"000\")\n\tx = strings.TrimSuffix(x, \".000\")\n\treturn x + \"Z\", nil\n}\n\nfunc marshalDuration(m protoreflect.Message) (string, error) {\n\tfds := m.Descriptor().Fields()\n\tfdSeconds := fds.ByNumber(durationSecondsFieldNumber)\n\tfdNanos := fds.ByNumber(durationNanosFieldNumber)","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/go-kratos/kratos/blob/668db92c2c001e9552594ba5a8aede8456af6d7e/encoding/form/well_known_types.go#L30-L66","documentation":"Form-encoding error while marshaling a google.protobuf.Timestamp to RFC 3339 string: the seconds component is outside the range this codec enforces before formatting (minTimestampSeconds..maxTimestampSeconds = 253402300799, i.e. year 9999). The check mirrors protojson's valid-timestamp window so that time.Unix formatting cannot overflow the RFC 3339 representation. The %s is google.protobuf.Timestamp and %v the offending seconds value.","triggerScenarios":"Encoding a Timestamp field via form/EncodeField where the message carries seconds beyond 253402300799 (after 9999-12-31) or below the minimum (before 0001-01-01): zero-struct Timestamps default to 0 (fine), but timestamps built from arithmetic overflow, int64 max, or uninitialized garbage (e.g. treating nanoseconds as seconds) trip it. Also timestamps parsed from strings like '99999-01-01'.","commonSituations":"Unit bugs: storing nanos/millis in the seconds field; sentinel values like math.MaxInt64 or -1 used as 'not set' in a Timestamp; copying a Duration's seconds into a Timestamp; legacy data with year > 9999 fed through timestamppb.New.","solutions":["Build Timestamps only via timestamppb.New(time.Time) so seconds/nanos are always consistent and in range","Fix unit bugs: verify the value you assign to seconds is actually seconds (divide millis/nanos before assigning)","Replace sentinel extremes (MaxInt64) with a nil/zero Timestamp or an optional wrapper to mean 'unset'","Clamp out-of-range values at the boundary (reject or cap to the 0001-9999 window) before encoding"],"exampleFix":"// before: ms stored as seconds -> seconds out of range\nts := &timestamppb.Timestamp{Seconds: time.Now().UnixNano() / 1e6}\n\n// after: use the constructor\nts := timestamppb.New(time.Now())","handlingStrategy":"validation","validationCode":"// Validate a Timestamp before encoding\nfunc validTimestamp(ts *timestamppb.Timestamp) bool {\n\tif ts == nil {\n\t\treturn true\n\t}\n\treturn ts.AsTime().Year() >= 1 && ts.AsTime().Year() <= 9999\n}","typeGuard":"func validTimestampSeconds(secs int64) bool {\n\treturn secs >= minTimestampSeconds && secs <= maxTimestampSeconds // 9999-12-31T23:59:59\n}","tryCatchPattern":"if _, err := form.EncodeField(fd, val); err != nil {\n\tif strings.Contains(err.Error(), \"seconds out of range\") {\n\t\t// data bug: log the source record and skip/clamp, do not abort the whole response\n\t\tlog.Error(\"bad timestamp in payload\", \"field\", fd.Name())\n\t\tcontinue\n\t}\n}","preventionTips":["Construct timestamps only with timestamppb.New(time.Time)","Never reuse math.MaxInt64 or -1 sentinels in Timestamp fields","Double-check unit conversions (ms/us/ns) wherever raw seconds are computed","Validate inbound timestamps at API boundaries before storing"],"tags":["go","kratos","form-binding","protobuf","timestamp","validation"],"backgroundTag":null,"analyzedSha":"668db92c2c001e9552594ba5a8aede8456af6d7e","analyzedAt":"2026-08-16T02:07:20.704Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}