docker/cli · error
cannot merge secrets
Error message
cannot merge secrets: %w
What it means
Raised inside the Compose multi-file merge loop when dario.cat/mergo fails to deep-merge the top-level `secrets` map of an override Compose file into the base. mergo.Map is invoked with WithOverride (merge.go:44), and any returned error is wrapped with this message. It indicates the override file's `secrets:` section has a structure incompatible with the base (e.g. a key that is a string in one file but an object in another).
Solutions
- Inspect the `secrets:` block of every Compose file passed to the merge and ensure each secret name uses the same form (both short string or both long object) across all files.
- Reduce the merge set to a single file to isolate which override introduces the conflict.
- Align all files to the long-form object syntax for secrets to guarantee type-consistent merges.
Example fix
// before (docker-compose.yml)
secrets:
dbpass: "./secrets/dbpass"
// before (docker-compose.override.yml)
secrets:
dbpass:
file: "./secrets/dbpass_alt"
// after (both files use long form)
secrets:
dbpass:
file: "./secrets/dbpass" Defensive patterns
Strategy: validation
Validate before calling
// Before merging, verify each secret entry has a consistent type across files.
func secretTypesSame(a, b map[string]any) bool {
for k, va := range a {
if vb, ok := b[k]; ok {
if reflect.TypeOf(va) != reflect.TypeOf(vb) {
return false
}
}
}
return true
} Try / catch
// loader.merge returns an error; wrap to surface the section.
merged, err := loader.merge(configs)
if err != nil {
return fmt.Errorf("compose merge failed: %w", err)
} Prevention
- Adopt a single canonical form (long-form objects) for all secrets across every Compose file.
- Run `docker compose config` in CI to validate merged output before deploy.
- Keep the number of override files small to reduce type-conflict surface.
When it happens
Trigger: Calling the internal `merge(configs []*types.Config)` with two or more parsed Compose configs where the `Secrets` maps have conflicting value types for the same secret name. mergo.Map returns an error when it cannot merge map values of non-overridable or mismatched kinds.
Common situations: A base docker-compose.yml defines a secret as a short-form string (e.g. `mysecret: file`) while an override (docker-compose.override.yml) redefines the same secret as a long-form object (`mysecret: { file: ... , environment: ...}`). Mixing the v2 short syntax with v3 long syntax for the same key triggers it.
Related errors
- cannot merge configs
- cannot merge service
- service
- undefined secret
- version mismatched between two composefiles
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/5e1024983a858e0d.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/loader/merge.go:45
}
func merge(configs []*types.Config) (*types.Config, error) {
base := configs[0]
for _, override := range configs[1:] {
var errs []error
if services, err := mergeServices(base.Services, override.Services); err != nil {
errs = append(errs, fmt.Errorf("cannot merge services: %w", err))
} else {
base.Services = services
}
if err := mergo.Map(&base.Volumes, &override.Volumes, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge volumes: %w", err))
}
if err := mergo.Map(&base.Networks, &override.Networks, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge networks: %w", err))
}
if err := mergo.Map(&base.Secrets, &override.Secrets, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge secrets: %w", err))
}
if err := mergo.Map(&base.Configs, &override.Configs, mergo.WithOverride); err != nil {
errs = append(errs, fmt.Errorf("cannot merge configs: %w", err))
}
if err := errors.Join(errs...); err != nil {
return nil, errors.Join(fmt.Errorf("failed to merge file %s", override.Filename), err)
}
}
return base, nil
}
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
mergeOpts := []func(*mergo.Config){
mergo.WithAppendSlice,
mergo.WithOverride,
mergo.WithTransformers(&specials{m: map[reflect.Type]func(dst, src reflect.Value) error{
reflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()): safelyMerge(mergeLoggingConfig),
reflect.TypeFor[[]types.ServicePortConfig](): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),View on GitHub (pinned to 4f84911bfe)