refactor: switch from multierr to errors.join

This commit is contained in:
Julien Neuhart
2026-03-27 22:14:01 +01:00
parent d3a65a587c
commit ebf0548d19
9 changed files with 60 additions and 58 deletions

1
go.mod
View File

@@ -32,7 +32,6 @@ require (
go.opentelemetry.io/otel/sdk/log v0.18.0 go.opentelemetry.io/otel/sdk/log v0.18.0
go.opentelemetry.io/otel/sdk/metric v1.42.0 go.opentelemetry.io/otel/sdk/metric v1.42.0
go.opentelemetry.io/otel/trace v1.42.0 go.opentelemetry.io/otel/trace v1.42.0
go.uber.org/multierr v1.11.0
golang.org/x/net v0.52.0 golang.org/x/net v0.52.0
golang.org/x/sync v0.20.0 golang.org/x/sync v0.20.0
golang.org/x/term v0.41.0 golang.org/x/term v0.41.0

2
go.sum
View File

@@ -313,8 +313,6 @@ go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpu
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw= go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=

View File

@@ -11,7 +11,6 @@ import (
"go.opentelemetry.io/otel" "go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"go.uber.org/multierr"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg/internal/log" "github.com/gotenberg/gotenberg/v8/pkg/gotenberg/internal/log"
internalotel "github.com/gotenberg/gotenberg/v8/pkg/gotenberg/internal/otel" internalotel "github.com/gotenberg/gotenberg/v8/pkg/gotenberg/internal/otel"
@@ -55,13 +54,13 @@ func (cfg TelemetryConfig) Validate() error {
var err error var err error
if cfg.ServiceName == "" { if cfg.ServiceName == "" {
err = multierr.Append(err, err = errors.Join(err,
errors.New("service name must not be empty"), errors.New("service name must not be empty"),
) )
} }
if cfg.ServiceVersion == "" { if cfg.ServiceVersion == "" {
err = multierr.Append(err, err = errors.Join(err,
errors.New("service version must not be empty"), errors.New("service version must not be empty"),
) )
} }
@@ -70,7 +69,7 @@ func (cfg TelemetryConfig) Validate() error {
case ErrorLoggingLevel, WarnLoggingLevel, InfoLoggingLevel, DebugLoggingLevel: case ErrorLoggingLevel, WarnLoggingLevel, InfoLoggingLevel, DebugLoggingLevel:
break break
default: default:
err = multierr.Append( err = errors.Join(
err, err,
fmt.Errorf("log level must be either %s, %s, %s or %s", ErrorLoggingLevel, WarnLoggingLevel, InfoLoggingLevel, DebugLoggingLevel), fmt.Errorf("log level must be either %s, %s, %s or %s", ErrorLoggingLevel, WarnLoggingLevel, InfoLoggingLevel, DebugLoggingLevel),
) )
@@ -80,7 +79,7 @@ func (cfg TelemetryConfig) Validate() error {
case AutoLoggingFormat, JsonLoggingFormat, TextLoggingFormat: case AutoLoggingFormat, JsonLoggingFormat, TextLoggingFormat:
break break
default: default:
err = multierr.Append( err = errors.Join(
err, err,
fmt.Errorf("standard log format must be either %s, %s or %s", AutoLoggingFormat, JsonLoggingFormat, TextLoggingFormat), fmt.Errorf("standard log format must be either %s, %s or %s", AutoLoggingFormat, JsonLoggingFormat, TextLoggingFormat),
) )

View File

@@ -15,7 +15,6 @@ import (
"github.com/dlclark/regexp2" "github.com/dlclark/regexp2"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"go.uber.org/multierr"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@@ -354,35 +353,35 @@ func (a *Api) Validate() error {
var err error var err error
if a.port < 1 || a.port > 65535 { if a.port < 1 || a.port > 65535 {
err = multierr.Append(err, err = errors.Join(err,
errors.New("port must be more than 1 and less than 65535"), errors.New("port must be more than 1 and less than 65535"),
) )
} }
if a.bindIp != "" && net.ParseIP(a.bindIp) == nil { if a.bindIp != "" && net.ParseIP(a.bindIp) == nil {
err = multierr.Append(err, errors.New("IP must be a valid IP address")) err = errors.Join(err, errors.New("IP must be a valid IP address"))
} }
if (a.tlsCertFile != "" && a.tlsKeyFile == "") || (a.tlsCertFile == "" && a.tlsKeyFile != "") { if (a.tlsCertFile != "" && a.tlsKeyFile == "") || (a.tlsCertFile == "" && a.tlsKeyFile != "") {
err = multierr.Append(err, err = errors.Join(err,
errors.New("both TLS certificate and key files must be set"), errors.New("both TLS certificate and key files must be set"),
) )
} }
if !strings.HasPrefix(a.rootPath, "/") { if !strings.HasPrefix(a.rootPath, "/") {
err = multierr.Append(err, err = errors.Join(err,
errors.New("root path must start with /"), errors.New("root path must start with /"),
) )
} }
if !strings.HasSuffix(a.rootPath, "/") { if !strings.HasSuffix(a.rootPath, "/") {
err = multierr.Append(err, err = errors.Join(err,
errors.New("root path must end with /"), errors.New("root path must end with /"),
) )
} }
if len(strings.TrimSpace(a.correlationIdHeader)) == 0 { if len(strings.TrimSpace(a.correlationIdHeader)) == 0 {
err = multierr.Append(err, err = errors.Join(err,
errors.New("trace header must not be empty"), errors.New("trace header must not be empty"),
) )
} }

View File

@@ -1,6 +1,7 @@
package api package api
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"net/http" "net/http"
@@ -12,8 +13,6 @@ import (
"strings" "strings"
"time" "time"
"go.uber.org/multierr"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg" "github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
) )
@@ -502,7 +501,7 @@ func (form *FormData) paths(extensions []string, target *[]string) *FormData {
// append adds an error to the list of errors. // append adds an error to the list of errors.
func (form *FormData) append(err error) { func (form *FormData) append(err error) {
form.errors = multierr.Append(form.errors, err) form.errors = errors.Join(form.errors, err)
} }
// mustValue binds the target interface with a form field. If the value is // mustValue binds the target interface with a form field. If the value is

View File

@@ -2,6 +2,7 @@ package chromium
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"net/http" "net/http"
@@ -17,7 +18,6 @@ import (
"github.com/chromedp/cdproto/runtime" "github.com/chromedp/cdproto/runtime"
"github.com/chromedp/chromedp" "github.com/chromedp/chromedp"
"github.com/dlclark/regexp2" "github.com/dlclark/regexp2"
"go.uber.org/multierr"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg" "github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
@@ -231,7 +231,7 @@ func listenForEventResponseReceived(
options.invalidResourceHttpStatusCodeMu.Lock() options.invalidResourceHttpStatusCodeMu.Lock()
defer options.invalidResourceHttpStatusCodeMu.Unlock() defer options.invalidResourceHttpStatusCodeMu.Unlock()
*options.invalidResourceHttpStatusCode = multierr.Append( *options.invalidResourceHttpStatusCode = errors.Join(
*options.invalidResourceHttpStatusCode, *options.invalidResourceHttpStatusCode,
fmt.Errorf("%s - %d: %s", ev.Response.URL, ev.Response.Status, http.StatusText(int(ev.Response.Status))), fmt.Errorf("%s - %d: %s", ev.Response.URL, ev.Response.Status, http.StatusText(int(ev.Response.Status))),
) )
@@ -334,7 +334,7 @@ func listenForEventLoadingFailed(ctx context.Context, logger *slog.Logger, optio
// We are looking for common errors. // We are looking for common errors.
// TODO: sufficient? // TODO: sufficient?
errors := []string{ knownErrors := []string{
"net::ERR_CONNECTION_CLOSED", "net::ERR_CONNECTION_CLOSED",
"net::ERR_CONNECTION_RESET", "net::ERR_CONNECTION_RESET",
"net::ERR_CONNECTION_REFUSED", "net::ERR_CONNECTION_REFUSED",
@@ -348,8 +348,8 @@ func listenForEventLoadingFailed(ctx context.Context, logger *slog.Logger, optio
"net::ERR_FILE_NOT_FOUND", "net::ERR_FILE_NOT_FOUND",
"net::ERR_HTTP2_PROTOCOL_ERROR", "net::ERR_HTTP2_PROTOCOL_ERROR",
} }
if !slices.Contains(errors, ev.ErrorText) { if !slices.Contains(knownErrors, ev.ErrorText) {
logger.DebugContext(ctx, fmt.Sprintf("skip EventLoadingFailed: '%s' is not part of %+v", ev.ErrorText, errors)) logger.DebugContext(ctx, fmt.Sprintf("skip EventLoadingFailed: '%s' is not part of %+v", ev.ErrorText, knownErrors))
return return
} }
@@ -378,7 +378,7 @@ func listenForEventLoadingFailed(ctx context.Context, logger *slog.Logger, optio
options.resourceLoadingFailedMu.Lock() options.resourceLoadingFailedMu.Lock()
defer options.resourceLoadingFailedMu.Unlock() defer options.resourceLoadingFailedMu.Unlock()
*options.resourceLoadingFailed = multierr.Append( *options.resourceLoadingFailed = errors.Join(
*options.resourceLoadingFailed, *options.resourceLoadingFailed,
fmt.Errorf("resource %s: %s", ev.Type, ev.ErrorText), fmt.Errorf("resource %s: %s", ev.Type, ev.ErrorText),
) )
@@ -397,7 +397,7 @@ func listenForEventExceptionThrown(ctx context.Context, logger *slog.Logger, con
consoleExceptionsMu.Lock() consoleExceptionsMu.Lock()
defer consoleExceptionsMu.Unlock() defer consoleExceptionsMu.Unlock()
*consoleExceptions = multierr.Append(*consoleExceptions, fmt.Errorf("\n%+v", ev.ExceptionDetails)) *consoleExceptions = errors.Join(*consoleExceptions, fmt.Errorf("\n%+v", ev.ExceptionDetails))
} }
}) })
} }

View File

@@ -17,7 +17,6 @@ import (
"github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
"go.uber.org/multierr"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg" "github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
"github.com/gotenberg/gotenberg/v8/pkg/modules/api" "github.com/gotenberg/gotenberg/v8/pkg/modules/api"
@@ -150,7 +149,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
for i, cookie := range cookies { for i, cookie := range cookies {
if strings.TrimSpace(cookie.Name) == "" || strings.TrimSpace(cookie.Value) == "" || strings.TrimSpace(cookie.Domain) == "" { if strings.TrimSpace(cookie.Name) == "" || strings.TrimSpace(cookie.Value) == "" || strings.TrimSpace(cookie.Domain) == "" {
err = multierr.Append(err, fmt.Errorf("cookie %d must have its name, value and domain set", i)) err = errors.Join(err, fmt.Errorf("cookie %d must have its name, value and domain set", i))
} }
} }
@@ -183,7 +182,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
if len(parts) == 2 && strings.ToLower(parts[0]) == "scope" && parts[1] != "" { if len(parts) == 2 && strings.ToLower(parts[0]) == "scope" && parts[1] != "" {
scope = parts[1] scope = parts[1]
} else { } else {
err = multierr.Append(err, fmt.Errorf("invalid scope '%s' for header '%s'", scope, k)) err = errors.Join(err, fmt.Errorf("invalid scope '%s' for header '%s'", scope, k))
invalidScopeToken = true invalidScopeToken = true
break break
} }
@@ -200,7 +199,7 @@ func FormDataChromiumOptions(ctx *api.Context) (*api.FormData, Options) {
if len(scope) > 0 { if len(scope) > 0 {
p, errCompile := regexp2.Compile(scope, regexp2.None) p, errCompile := regexp2.Compile(scope, regexp2.None)
if errCompile != nil { if errCompile != nil {
err = multierr.Append(err, fmt.Errorf("invalid scope regex pattern for header '%s': %w", k, errCompile)) err = errors.Join(err, fmt.Errorf("invalid scope regex pattern for header '%s': %w", k, errCompile))
continue continue
} }
scopeRegexp = p scopeRegexp = p
@@ -676,7 +675,7 @@ func markdownToHtml(ctx *api.Context, inputPath string, markdownPaths []string)
} }
if path == "" { if path == "" {
markdownFilesNotFoundErr = multierr.Append( markdownFilesNotFoundErr = errors.Join(
markdownFilesNotFoundErr, markdownFilesNotFoundErr,
fmt.Errorf("'%s'", filename), fmt.Errorf("'%s'", filename),
) )

View File

@@ -18,7 +18,6 @@ import (
"go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.40.0" semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"go.uber.org/multierr"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg" "github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
"github.com/gotenberg/gotenberg/v8/pkg/modules/api" "github.com/gotenberg/gotenberg/v8/pkg/modules/api"
@@ -468,12 +467,12 @@ func (a *Api) Validate() error {
_, statErr := os.Stat(a.args.binPath) _, statErr := os.Stat(a.args.binPath)
if os.IsNotExist(statErr) { if os.IsNotExist(statErr) {
err = multierr.Append(err, fmt.Errorf("LibreOffice binary path does not exist: %w", statErr)) err = errors.Join(err, fmt.Errorf("LibreOffice binary path does not exist: %w", statErr))
} }
_, statErr = os.Stat(a.args.unoBinPath) _, statErr = os.Stat(a.args.unoBinPath)
if os.IsNotExist(statErr) { if os.IsNotExist(statErr) {
err = multierr.Append(err, fmt.Errorf("unoconverter binary path does not exist: %w", statErr)) err = errors.Join(err, fmt.Errorf("unoconverter binary path does not exist: %w", statErr))
} }
return err return err

View File

@@ -2,13 +2,13 @@ package pdfengines
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"sync" "sync"
"go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
"go.uber.org/multierr"
"github.com/gotenberg/gotenberg/v8/pkg/gotenberg" "github.com/gotenberg/gotenberg/v8/pkg/gotenberg"
) )
@@ -80,8 +80,9 @@ func (multi *multiPdfEngines) Merge(ctx context.Context, logger *slog.Logger, in
select { select {
case mergeErr := <-errChan: case mergeErr := <-errChan:
errored := multierr.AppendInto(&err, mergeErr) if mergeErr != nil {
if !errored { err = errors.Join(err, mergeErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -124,7 +125,7 @@ func (multi *multiPdfEngines) Split(ctx context.Context, logger *slog.Logger, mo
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {
mu.Lock() mu.Lock()
err = multierr.Append(err, result.err) err = errors.Join(err, result.err)
mu.Unlock() mu.Unlock()
} else { } else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
@@ -159,8 +160,9 @@ func (multi *multiPdfEngines) Flatten(ctx context.Context, logger *slog.Logger,
select { select {
case mergeErr := <-errChan: case mergeErr := <-errChan:
errored := multierr.AppendInto(&err, mergeErr) if mergeErr != nil {
if !errored { err = errors.Join(err, mergeErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -193,8 +195,9 @@ func (multi *multiPdfEngines) Convert(ctx context.Context, logger *slog.Logger,
select { select {
case mergeErr := <-errChan: case mergeErr := <-errChan:
errored := multierr.AppendInto(&err, mergeErr) if mergeErr != nil {
if !errored { err = errors.Join(err, mergeErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -239,7 +242,7 @@ func (multi *multiPdfEngines) ReadMetadata(ctx context.Context, logger *slog.Log
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {
mu.Lock() mu.Lock()
err = multierr.Append(err, result.err) err = errors.Join(err, result.err)
mu.Unlock() mu.Unlock()
} else { } else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
@@ -274,8 +277,9 @@ func (multi *multiPdfEngines) WriteMetadata(ctx context.Context, logger *slog.Lo
select { select {
case writeMetadataErr := <-errChan: case writeMetadataErr := <-errChan:
errored := multierr.AppendInto(&err, writeMetadataErr) if writeMetadataErr != nil {
if !errored { err = errors.Join(err, writeMetadataErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -318,7 +322,7 @@ func (multi *multiPdfEngines) PageCount(ctx context.Context, logger *slog.Logger
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {
mu.Lock() mu.Lock()
err = multierr.Append(err, result.err) err = errors.Join(err, result.err)
mu.Unlock() mu.Unlock()
} else { } else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
@@ -365,7 +369,7 @@ func (multi *multiPdfEngines) ReadBookmarks(ctx context.Context, logger *slog.Lo
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {
mu.Lock() mu.Lock()
err = multierr.Append(err, result.err) err = errors.Join(err, result.err)
mu.Unlock() mu.Unlock()
} else { } else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
@@ -400,8 +404,9 @@ func (multi *multiPdfEngines) WriteBookmarks(ctx context.Context, logger *slog.L
select { select {
case writeBookmarksErr := <-errChan: case writeBookmarksErr := <-errChan:
errored := multierr.AppendInto(&err, writeBookmarksErr) if writeBookmarksErr != nil {
if !errored { err = errors.Join(err, writeBookmarksErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -434,8 +439,9 @@ func (multi *multiPdfEngines) Encrypt(ctx context.Context, logger *slog.Logger,
select { select {
case protectErr := <-errChan: case protectErr := <-errChan:
errored := multierr.AppendInto(&err, protectErr) if protectErr != nil {
if !errored { err = errors.Join(err, protectErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -470,8 +476,9 @@ func (multi *multiPdfEngines) EmbedFiles(ctx context.Context, logger *slog.Logge
select { select {
case embedErr := <-errChan: case embedErr := <-errChan:
errored := multierr.AppendInto(&err, embedErr) if embedErr != nil {
if !errored { err = errors.Join(err, embedErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -506,8 +513,9 @@ func (multi *multiPdfEngines) Watermark(ctx context.Context, logger *slog.Logger
select { select {
case watermarkErr := <-errChan: case watermarkErr := <-errChan:
errored := multierr.AppendInto(&err, watermarkErr) if watermarkErr != nil {
if !errored { err = errors.Join(err, watermarkErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -542,8 +550,9 @@ func (multi *multiPdfEngines) Stamp(ctx context.Context, logger *slog.Logger, in
select { select {
case stampErr := <-errChan: case stampErr := <-errChan:
errored := multierr.AppendInto(&err, stampErr) if stampErr != nil {
if !errored { err = errors.Join(err, stampErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }
@@ -576,8 +585,9 @@ func (multi *multiPdfEngines) Rotate(ctx context.Context, logger *slog.Logger, i
select { select {
case rotateErr := <-errChan: case rotateErr := <-errChan:
errored := multierr.AppendInto(&err, rotateErr) if rotateErr != nil {
if !errored { err = errors.Join(err, rotateErr)
} else {
span.SetStatus(codes.Ok, "") span.SetStatus(codes.Ok, "")
return nil return nil
} }