{"record":{"id":"aabf61dc2f6ec67c","repo":"temporalio/temporal","slug":"v-w","errorCode":null,"errorMessage":"%v, %w","messagePattern":"%v, %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"common/rpc/encryption/local_store_cert_provider.go","lineNumber":496,"sourceCode":"\t\tif certDERBlock.Type == \"CERTIFICATE\" {\n\t\t\tcertBytes = append(certBytes, certDERBlock.Bytes)\n\t\t}\n\t}\n\n\tif len(certBytes) == 0 || len(certBytes[0]) == 0 {\n\t\treturn nil, fmt.Errorf(\"failed to decode PEM certificate data\")\n\t}\n\treturn x509.ParseCertificate(certBytes[0])\n}\n\nfunc appendError(aggregatedErr error, err error) error {\n\tif aggregatedErr == nil {\n\t\treturn err\n\t}\n\tif err == nil {\n\t\treturn aggregatedErr\n\t}\n\treturn fmt.Errorf(\"%v, %w\", aggregatedErr, err)\n}\n\nfunc (s *localStoreCertProvider) refreshCerts() {\n\n\tfor {\n\t\tselect {\n\t\tcase <-s.stop:\n\t\t\treturn\n\t\tcase <-s.ticker.C:\n\t\t}\n\n\t\tnewCerts, err := s.loadCerts()\n\t\tif err != nil {\n\t\t\ts.logger.Error(\"failed to load certificates\", tag.Error(err))\n\t\t\tcontinue\n\t\t}\n\n\t\ts.RLock()","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/rpc/encryption/local_store_cert_provider.go#L478-L514","documentation":"appendError aggregates two errors: returns whichever one is nil, and when both are non-nil returns fmt.Errorf(\"%v, %v\", aggregatedErr, err) — effectively joining them with \", \". The %w on the second error preserves the wrapped chain for errors.Is/As on the appended error. It is a helper for reporting accumulated cert-expiry/refresh problems, not a domain failure.","triggerScenarios":"Returned whenever both inputs are non-nil: GetExpiringCerts aggregating multiple certificate parsing/listing errors, or TestAppendError directly. Seeing this message text in output means at least two underlying errors were joined.","commonSituations":"A cert store with several expiring or corrupt certificates where the first error is the original message and the second is a subsequent failure during the same scan; reading the joined string and not realizing it contains two distinct problems.","solutions":["Read the message as two comma-separated errors and address each underlying cause individually.","Use errors.Is/errors.As against the second (wrapped) error to identify the root cause programmatically.","Fix the primary (left-hand) error first; it is reported unwrapped via %v so the first error's chain is not preserved."],"exampleFix":"// before\nif err != nil { return err } // losing one of the joined errors\n// after\nif errors.Is(err, os.ErrNotExist) || strings.Contains(err.Error(), \"failed to decode PEM\") { /* handle underlying cause */ }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func unwrapJoined(err error) []error {\n    var out []error\n    for err != nil {\n        out = append(out, err)\n        err = errors.Unwrap(err)\n    }\n    return out\n}","tryCatchPattern":"if err != nil {\n    var target error\n    if errors.As(err, &target) { /* inspect joined/wrapped cause */ }\n    log.Warn(\"aggregated cert errors\", \"detail\", err.Error())\n}","preventionTips":["Treat comma-joined messages as multiple failures, not one.","Prefer errors.Is/As over string matching on the joined text.","Monitor cert-expiry warnings before they become refresh errors."],"tags":["errors","error-aggregation","tls"],"backgroundTag":"aggregated-error-message","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}