{"record":{"id":"57a3e9d4f3e9e0ff","repo":"jaegertracing/jaeger","slug":"remote-storage-only-supports-a-single-storage-back","errorCode":null,"errorMessage":"remote-storage only supports a single storage backend, but %d were configured","messagePattern":"remote-storage only supports a single storage backend, but (.+?) were configured","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/remote-storage/app/config.go","lineNumber":53,"sourceCode":"\n\t// Validate storage configuration\n\tif err := cfg.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid configuration: %w\", err)\n\t}\n\n\treturn cfg, nil\n}\n\n// Validate validates the configuration.\nfunc (c *Config) Validate() error {\n\t// Validate storage configuration\n\tif err := c.Storage.Validate(); err != nil {\n\t\treturn err\n\t}\n\n\t// Ensure only one backend is defined for remote-storage\n\tif len(c.Storage.TraceBackends) > 1 {\n\t\treturn fmt.Errorf(\"remote-storage only supports a single storage backend, but %d were configured\", len(c.Storage.TraceBackends))\n\t}\n\n\treturn nil\n}\n\n// GetStorageName returns the name of the first configured storage backend.\n// This is used as the default storage when not otherwise specified.\nfunc (c *Config) GetStorageName() string {\n\tfor name := range c.Storage.TraceBackends {\n\t\treturn name\n\t}\n\treturn \"\"\n}\n\n// DefaultConfig returns a default configuration with memory storage.\n// This is used when no configuration file is provided.\nfunc DefaultConfig() *Config {\n\treturn &Config{","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/cmd/remote-storage/app/config.go#L35-L71","documentation":"Config.Validate enforces that the remote-storage service has exactly one storage backend. The remote-storage gRPC server serves a single trace backend, so unlike the main `jaeger` binary it rejects configurations that define multiple entries in storage.trace_backends, reporting how many were found.","triggerScenarios":"Calling Validate (directly or via LoadConfigFromViper) when len(c.Storage.TraceBackends) > 1 — i.e. two or more named backends are present in the `storage.trace_backends` map.","commonSituations":"Reusing a main-jaeger config file that defines several backends (e.g. cassandra + opensearch) for the remote-storage binary; merging multiple config fragments whose backend maps combine into more than one entry.","solutions":["Keep only one entry under storage.trace_backends in the remote-storage config","Move any additional backends to the main `jaeger` binary's config, which supports multiple backends","If multiple configs are merged, scope viper keys so only the intended backend lands under storage.trace_backends"],"exampleFix":"# before (2 backends)\nstorage:\n  trace_backends:\n    memory: {memory: {max_traces: 1000000}}\n    cassandra: {cassandra: {servers: [cassandra:9042]}}\n# after (1 backend)\nstorage:\n  trace_backends:\n    memory: {memory: {max_traces: 1000000}}","handlingStrategy":"validation","validationCode":"func ensureSingleBackend(v *viper.Viper) error {\n    backends := v.GetStringMap(\"storage.trace_backends\")\n    if len(backends) > 1 {\n        names := make([]string, 0, len(backends))\n        for n := range backends { names = append(names, n) }\n        return fmt.Errorf(\"remove extra backends, keep one of: %v\", names)\n    }\n    return nil\n}\n// call before LoadConfigFromViper","typeGuard":null,"tryCatchPattern":"if _, err := app.LoadConfigFromViper(v); err != nil {\n    var cfgErr *fmt.WrapError // or match on message\n    if strings.Contains(err.Error(), \"single storage backend\") {\n        fmt.Fprintf(os.Stderr, \"fix storage.trace_backends: %v\\n\", err)\n        os.Exit(1)\n    }\n    return err\n}","preventionTips":["Audit merged config fragments so they don't each add a backend","Use remote-storage only for the single-backend serving role; multi-backend belongs to the main jaeger binary","Add a startup-time count check on storage.trace_backends in deployment tooling","Keep remote-storage configs minimal and generated from DefaultConfig() as a template"],"tags":["go","configuration","validation","remote-storage"],"backgroundTag":"multiple-storage-backends-configured","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}