From 988e07ade555fea2cf0286746cba30541ce42211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ask=20Bj=C3=B8rn=20Hansen?= Date: Sat, 2 May 2026 22:00:09 -0700 Subject: [PATCH] fix(tracing): treat resource detector errors as non-fatal processOwnerDetector calls os/user.Current(), which fails in non-cgo builds when the running UID has no /etc/passwd entry and $USER is unset (common on hardened/distroless containers). It returns a plain error, not ErrPartialResource, so the previous gate let the error escape and SetupSDK aborted process startup. Resource detection is documented as best-effort; downgrade any detector error to a warning and fall back to an empty resource if the SDK returns nil. The signature is preserved. --- internal/otlpresource/resource.go | 24 +++++++++++++++--------- scripts/run-goreleaser | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/internal/otlpresource/resource.go b/internal/otlpresource/resource.go index a228d6c..3bc5eb8 100644 --- a/internal/otlpresource/resource.go +++ b/internal/otlpresource/resource.go @@ -6,7 +6,6 @@ package otlpresource import ( "context" - "errors" "log/slog" "go.opentelemetry.io/otel/attribute" @@ -21,8 +20,11 @@ type Options struct { Environment string // added as attribute "environment" } -// New builds the shared OTel resource. Non-fatal partial/schema errors are -// logged via the provided slog logger and a usable resource is still returned. +// New builds the shared OTel resource. Resource detection is best-effort: +// any detector error is logged via the provided slog logger and a usable +// resource is still returned. The returned error is currently always nil; the +// signature is preserved so future callers can distinguish hard failures if +// the contract changes. func New(ctx context.Context, log *slog.Logger, opts Options) (*resource.Resource, error) { detectors := []resource.Option{ resource.WithFromEnv(), // OTEL_SERVICE_NAME / OTEL_RESOURCE_ATTRIBUTES @@ -41,12 +43,16 @@ func New(ctx context.Context, log *slog.Logger, opts Options) (*resource.Resourc resource.WithAttributes(attribute.String("environment", opts.Environment))) } + // Detector failures (e.g. processOwnerDetector hitting user.Current() in + // a non-cgo build with no /etc/passwd entry and unset $USER) must not be + // fatal. Downgrade everything to a warning and fall back to an empty + // resource if the SDK returns nil. res, err := resource.New(ctx, detectors...) - if errors.Is(err, resource.ErrPartialResource) || errors.Is(err, resource.ErrSchemaURLConflict) { - if log != nil { - log.Warn("otel resource setup", "err", err) - } - return res, nil + if err != nil && log != nil { + log.Warn("otel resource setup", "err", err) } - return res, err + if res == nil { + res = resource.Empty() + } + return res, nil } diff --git a/scripts/run-goreleaser b/scripts/run-goreleaser index 9191d6b..c549685 100755 --- a/scripts/run-goreleaser +++ b/scripts/run-goreleaser @@ -2,7 +2,7 @@ set -euo pipefail -go install github.com/goreleaser/goreleaser/v2@v2.14.1 +go install github.com/goreleaser/goreleaser/v2@v2.15.4 if [ ! -z "${harbor_username:-}" ]; then DOCKER_FILE=~/.docker/config.json