{"record":{"id":"8985115454b9fdb6","repo":"temporalio/temporal","slug":"can-t-deep-copy-non-zero-time-time-v","errorCode":null,"errorMessage":"Can't deep copy non-zero time.Time: %v","messagePattern":"Can't deep copy non-zero time\\.Time: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/dynamicconfig/deepcopy.go","lineNumber":61,"sourceCode":"\t\treturn deepCopyValue(v.Elem()).Addr()\n\tcase reflect.Slice:\n\t\tif v.IsNil() {\n\t\t\treturn v\n\t\t}\n\t\tnv := reflect.MakeSlice(v.Type(), v.Len(), v.Len())\n\t\tfor i := range v.Len() {\n\t\t\tnv.Index(i).Set(deepCopyValue(v.Index(i)))\n\t\t}\n\t\treturn nv\n\tcase reflect.Struct:\n\t\t// Special case for time.Time: it has unexported fields so we can't copy it field by\n\t\t// field, but we can copy zero values (which is all we need for default values).\n\t\tif v.Type() == reflect.TypeFor[time.Time]() {\n\t\t\tif v.Interface().(time.Time).IsZero() {\n\t\t\t\treturn reflect.ValueOf(time.Time{})\n\t\t\t}\n\t\t\t// nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code\n\t\t\tpanic(fmt.Sprintf(\"Can't deep copy non-zero time.Time: %v\", v.Interface()))\n\t\t}\n\t\tnv := reflect.New(v.Type()).Elem()\n\t\tfor i := range v.Type().NumField() {\n\t\t\tnv.Field(i).Set(deepCopyValue(v.Field(i)))\n\t\t}\n\t\treturn nv\n\tcase reflect.Interface, reflect.Func, reflect.Chan:\n\t\t// only nil values of any other reference types allowed!\n\t\tif v.IsNil() {\n\t\t\treturn v\n\t\t}\n\t\tfallthrough\n\tdefault:\n\t\t// nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code\n\t\tpanic(fmt.Sprintf(\"Can't deep copy value of type %s: %v\", v.Type(), v))\n\t}\n}\n","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/dynamicconfig/deepcopy.go#L43-L79","documentation":"The dynamicconfig deep-copy machinery (used to isolate config values before mapstructure decoding) can only deep-copy zero time.Time values, since time.Time contains unexported fields. Copying a non-zero time.Time is impossible with this reflection approach, so it panics. The nolint note says this is expected to trigger only from static initializers, never production traffic.","triggerScenarios":"Declaring a dynamic config setting whose default value (or struct field inside a default struct) contains a non-zero time.Time, evaluated during package init when deepCopyForMapstructure/deepCopyValue runs.","commonSituations":"A developer adds a default like `time.Now()` or a populated timestamp to a config struct used as a dynamicconfig default; a struct default gains a new time.Time field that is initialized non-zero.","solutions":["Change the default value to the zero time.Time{} and populate the timestamp at read/usage time","Use a different type (e.g. unix seconds/milliseconds int64 or a duration) for time-like config fields","Set the field only after decoding the config, not in the static default"],"exampleFix":"// before\nvar cfg = NewDefaultSetting(..., Setting{Deadline: time.Now()})\n// after\nvar cfg = NewDefaultSetting(..., Setting{Deadline: time.Time{}}) // set real value after decode","handlingStrategy":"validation","validationCode":"// ensure default structs contain only zero time.Time","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Zero-value time.Time in defaults","Use durations/epoch ints instead"],"tags":["go","panic","reflection","dynamicconfig","deep-copy"],"backgroundTag":"unsupported-deep-copy-type","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}