{"record":{"id":"917bf61cd35d9af7","repo":"temporalio/temporal","slug":"unreachable","errorCode":null,"errorMessage":"unreachable","messagePattern":"unreachable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/nexus/nexusrpc/api.go","lineNumber":275,"sourceCode":"\tm := durationRegexp.FindStringSubmatch(value)\n\tif len(m) == 0 {\n\t\treturn 0, fmt.Errorf(\"invalid duration: %q\", value)\n\t}\n\tv, err := strconv.ParseFloat(m[1], 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tswitch m[2] {\n\tcase \"ms\":\n\t\treturn time.Millisecond * time.Duration(v), nil\n\tcase \"s\":\n\t\treturn time.Millisecond * time.Duration(v*1e3), nil\n\tcase \"m\":\n\t\treturn time.Millisecond * time.Duration(v*1e3*60), nil\n\t}\n\t// nolint:forbidigo // code is unreachable due to regex validation\n\tpanic(\"unreachable\")\n}\n\n// FormatDuration converts a duration into a string representation in millisecond resolution.\nfunc FormatDuration(d time.Duration) string {\n\treturn strconv.FormatInt(d.Milliseconds(), 10) + \"ms\"\n}\n\nconst wrapperErrorMetadataKey = \"unwrap-error\"\n\n// MarkAsWrapperError adds the wrapper-error metadata to the original failure of the given OperationError, which\n// signals the Temporal codebase to unwrap the underlying failure as the failure cause.\n// This is used as a shim for Temporal->Temporal calls. Temporal already has a Failure type that represents an\n// OperationError and does not need to record this wrapper error.\nfunc MarkAsWrapperError(failureConverter FailureConverter, opErr *nexus.OperationError) error {\n\toriginalFailure, err := failureConverter.ErrorToFailure(opErr)\n\tif err != nil {\n\t\treturn err\n\t}","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/nexus/nexusrpc/api.go#L257-L293","documentation":"ParseDuration parses Nexus timeout header strings (e.g. \"5m\", \"300ms\") whose units are pre-validated by a regex. If input reaches the unit switch with a unit the switch does not handle, the function panics with \"unreachable\". The code assumes regex validation guarantees only ms/s/m units ever arrive, so hitting this panic means the validation regex and the switch have drifted apart.","triggerScenarios":"Calling ParseDuration directly (as tests do) with a duration string whose unit is not \"ms\", \"s\", or \"m\" (e.g. \"5h\", \"5d\"), bypassing the regex validation normally done by parseRequestTimeoutHeader.","commonSituations":"A new unit is added to the validation regex but not to the switch (or vice versa); direct unit tests feed unvalidated strings; another caller reuses ParseDuration without first running the header regex.","solutions":["Ensure every caller validates the input with the same regex used by parseRequestTimeoutHeader before calling ParseDuration","If you need more units (h, d), add matching cases to the switch and update the regex together","If the regex was changed, update ParseDuration's switch in common/nexus/nexusrpc/api.go to cover the new units"],"exampleFix":"// before\npanic(\"unreachable\")\n// after\nreturn 0, serviceerror.NewInvalidArgument(fmt.Sprintf(\"invalid duration unit: %q\", unit))","handlingStrategy":"validation","validationCode":"var durationRe = regexp.MustCompile(`^\\d+(ms|s|m)$`)\nfunc canParseDuration(s string) bool { return durationRe.MatchString(s) }\nif !canParseDuration(input) {\n\treturn serviceerror.NewInvalidArgument(\"invalid timeout duration\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run the header regex validation before calling ParseDuration","Never feed user-supplied strings to ParseDuration unvalidated","Keep the validation regex and ParseDuration's unit switch in sync when adding units","Add a table-driven unit test covering every regex-accepted unit"],"tags":["go","nexus","parsing","panic"],"backgroundTag":"unhandled-duration-unit","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}