docker/cli · error
the external-ca option needs a protocol= parameter
Error message
the external-ca option needs a protocol= parameter
What it means
Thrown by parseExternalCA (cli/command/swarm/opts.go:210) when an `--external-ca` specification contains no `protocol=` key. An external CA spec is incomplete without a protocol (currently only 'cfssl' is recognized), so parsing aborts after the field loop if hasProtocol is false.
Solutions
- Add `protocol=cfssl` to the spec: `--external-ca protocol=cfssl,url=https://ca.example.com`.
- Validate the spec string contains `protocol=` before invoking the command.
Example fix
// before docker swarm update --external-ca url=https://ca.example.com // after docker swarm update --external-ca protocol=cfssl,url=https://ca.example.com
Defensive patterns
Strategy: validation
Validate before calling
// Require protocol= in the external-ca spec string.
if !strings.Contains(spec, "protocol=") {
return errors.New("external-ca spec needs protocol= (e.g. cfssl)")
} Prevention
- Build external-ca specs from a struct with required fields rather than raw strings.
- Unit-test the spec formatter against missing keys.
- Document the mandatory protocol= and url= keys together.
When it happens
Trigger: Passing `--external-ca url=https://ca.example.com` (protocol omitted) or `--external-ca cacert=/x` without a protocol key.
Common situations: Forgot the protocol key; assumed a default; malformed spec string from a template.
Related errors
- CA cert for external CA must be in PEM format
- the external-ca option needs a url= parameter
- other flags may not be combined with --rollback
- duplicate mount target
- unknown role
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/7c6a3f790d240139.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/swarm/opts.go:210
case "url":
hasURL = true
externalCA.URL = value
case "cacert":
cacontents, err := os.ReadFile(value)
if err != nil {
return nil, fmt.Errorf("unable to read CA cert for external CA: %w", err)
}
if pemBlock, _ := pem.Decode(cacontents); pemBlock == nil {
return nil, errors.New("CA cert for external CA must be in PEM format")
}
externalCA.CACert = string(cacontents)
default:
externalCA.Options[key] = value
}
}
if !hasProtocol {
return nil, errors.New("the external-ca option needs a protocol= parameter")
}
if !hasURL {
return nil, errors.New("the external-ca option needs a url= parameter")
}
return &externalCA, nil
}
func addSwarmCAFlags(flags *pflag.FlagSet, options *swarmCAOptions) {
flags.DurationVar(&options.nodeCertExpiry, flagCertExpiry, 90*24*time.Hour, "Validity period for node certificates (ns|us|ms|s|m|h)")
flags.Var(&options.externalCA, flagExternalCA, "Specifications of one or more certificate signing endpoints")
}
func addSwarmFlags(flags *pflag.FlagSet, options *swarmOptions) {
flags.Int64Var(&options.taskHistoryLimit, flagTaskHistoryLimit, 5, "Task history retention limit")
flags.DurationVar(&options.dispatcherHeartbeat, flagDispatcherHeartbeat, 5*time.Second, "Dispatcher heartbeat period (ns|us|ms|s|m|h)")
flags.Uint64Var(&options.maxSnapshots, flagMaxSnapshots, 0, "Number of additional Raft snapshots to retain")
flags.SetAnnotation(flagMaxSnapshots, "version", []string{"1.25"})View on GitHub (pinned to 4f84911bfe)