jaegertracing/jaeger · error

failed to unmarshal configuration: %w

Error message

failed to unmarshal configuration: %w

What it means

LoadConfigFromViper unmarshals the entire Viper state into the remote-storage Config struct (GRPC server, tenancy, storage backends). Viper's Unmarshal relies on mapstructure; when the YAML/env values cannot be decoded into the target fields (wrong types, unknown keys with strict decoding, bad nested structures) this error wraps and re-raises the decode failure.

Source

Thrown at cmd/remote-storage/app/config.go:33

	"github.com/jaegertracing/jaeger/internal/tenancy"
)

// Config represents the configuration for remote-storage service.
type Config struct {
	GRPC    configgrpc.ServerConfig `mapstructure:"grpc"`
	Tenancy tenancy.Options         `mapstructure:"multi_tenancy"`
	// This configuration is the same as of the main `jaeger` binary,
	// but only one backend should be defined.
	Storage storageconfig.Config `mapstructure:"storage"`
}

// LoadConfigFromViper loads the configuration from Viper.
func LoadConfigFromViper(v *viper.Viper) (*Config, error) {
	cfg := &Config{}

	// Unmarshal the entire configuration
	if err := v.Unmarshal(cfg); err != nil {
		return nil, fmt.Errorf("failed to unmarshal configuration: %w", err)
	}

	// Validate storage configuration
	if err := cfg.Validate(); err != nil {
		return nil, fmt.Errorf("invalid configuration: %w", err)
	}

	return cfg, nil
}

// Validate validates the configuration.
func (c *Config) Validate() error {
	// Validate storage configuration
	if err := c.Storage.Validate(); err != nil {
		return err
	}

	// Ensure only one backend is defined for remote-storage

View on GitHub (pinned to 806f444784)

Solutions

  1. Read the wrapped mapstructure error — it names the exact field and expected type
  2. Fix the YAML/env value type to match the Config struct (e.g. quote-free ints, correct nesting under grpc/multi_tenancy/storage)
  3. Validate the config file against the current Jaeger version's remote-storage schema; migrate old keys
  4. Start from the documented sample remote-storage config and re-apply your overrides

Example fix

# before
storage:
  trace_backends: "cassandra"   # string, must be a map
# after
storage:
  trace_backends:
    cassandra:
      cassandra:
        servers: [cassandra:9042]
Defensive patterns

Strategy: validation

Validate before calling

func validateRemoteStorageKeys(v *viper.Viper) error {
    for _, key := range []string{"grpc", "storage"} {
        if v.Get(key) == nil { return fmt.Errorf("missing required section %q", key) }
    }
    if _, ok := v.Get("storage.trace_backends").(map[string]any); !ok {
        return fmt.Errorf("storage.trace_backends must be a map of backend name -> config")
    }
    return nil
}

Try / catch

cfg, err := app.LoadConfigFromViper(v)
if err != nil {
    var decodeErr mapstructure.Error
    if errors.As(err, &decodeErr) {
        for _, e := range decodeErr.Errors { fmt.Fprintln(os.Stderr, "config: ", e) }
    }
    return fmt.Errorf("loading remote-storage config: %w", err)
}

Prevention

When it happens

Trigger: Calling LoadConfigFromViper with a viper instance whose settings do not match the Config schema — e.g. grpc port given as a string where a struct field is expected, multi_tenancy shaped incorrectly, or a storage backend field of the wrong type.

Common situations: Typo'd or misindented remote-storage config file; passing raw env vars of string type into numeric/struct fields; upgrading Jaeger where the storage config schema changed (single-backend `storage` layout) while using an old config; supplying full `jaeger` multi-backend storage config to the remote-storage binary.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/f7705487beb5d514. Report an issue: GitHub.