temporalio/temporal · info
%v, %w
Error message
%v, %w
What it means
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.
Source
Thrown at common/rpc/encryption/local_store_cert_provider.go:496
if certDERBlock.Type == "CERTIFICATE" {
certBytes = append(certBytes, certDERBlock.Bytes)
}
}
if len(certBytes) == 0 || len(certBytes[0]) == 0 {
return nil, fmt.Errorf("failed to decode PEM certificate data")
}
return x509.ParseCertificate(certBytes[0])
}
func appendError(aggregatedErr error, err error) error {
if aggregatedErr == nil {
return err
}
if err == nil {
return aggregatedErr
}
return fmt.Errorf("%v, %w", aggregatedErr, err)
}
func (s *localStoreCertProvider) refreshCerts() {
for {
select {
case <-s.stop:
return
case <-s.ticker.C:
}
newCerts, err := s.loadCerts()
if err != nil {
s.logger.Error("failed to load certificates", tag.Error(err))
continue
}
s.RLock()View on GitHub (pinned to bde624efd1)
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.
Example fix
// before
if err != nil { return err } // losing one of the joined errors
// after
if errors.Is(err, os.ErrNotExist) || strings.Contains(err.Error(), "failed to decode PEM") { /* handle underlying cause */ } Defensive patterns
Strategy: type-guard
Type guard
func unwrapJoined(err error) []error {
var out []error
for err != nil {
out = append(out, err)
err = errors.Unwrap(err)
}
return out
} Try / catch
if err != nil {
var target error
if errors.As(err, &target) { /* inspect joined/wrapped cause */ }
log.Warn("aggregated cert errors", "detail", err.Error())
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- URI is invalid
- URI scheme does not match the archiver
- history was mutated
- get archived history request is invalid
- query visiblity request is invalid
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/aabf61dc2f6ec67c.
Report an issue: GitHub.