docker/cli · error

unrecognized external CA protocol

Error message

unrecognized external CA protocol %s

What it means

Raised by parseExternalCA when the protocol= field is not 'cfssl' (case-insensitive). The only supported ExternalCAProtocol value is swarm.ExternalCAProtocolCFSSL; any other value is rejected.

Solutions

  1. Use protocol=cfssl, which is the only supported external CA protocol in this version.
  2. Point the cfssl URL at your own signing service that speaks the cfssl API.
  3. If you need a different CA integration, use a sidecar that exposes the cfssl protocol.

Example fix

# before
docker swarm init --external-ca protocol=vault,url=https://vault:8200

# after
docker swarm init --external-ca protocol=cfssl,url=https://my-cfssl-signer:8888
Defensive patterns

Strategy: validation

Validate before calling

// Validate protocol before parse
if !strings.EqualFold(proto, string(swarm.ExternalCAProtocolCFSSL)) {
    return fmt.Errorf("unsupported external CA protocol %q; only cfssl", proto)
}

Type guard

func isSupportedCAProtocol(s string) bool {
	return strings.EqualFold(s, string(swarm.ExternalCAProtocolCFSSL))
}

Prevention

When it happens

Trigger: Passing '--external-ca protocol=vault,url=...' or protocol=https, etc. Only 'cfssl' is accepted at opts.go:187-191.

Common situations: Operator assumes HashiCorp Vault or a generic HTTPS CA is supported directly; case mismatch is not the issue (it's lowercased), only the value matters.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/38191d23749f437f. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/swarm/opts.go:190

	var (
		hasProtocol bool
		hasURL      bool
	)

	for _, field := range fields {
		key, value, ok := strings.Cut(field, "=")
		if !ok {
			return nil, fmt.Errorf("invalid field '%s' must be a key=value pair", field)
		}

		// TODO(thaJeztah): these options should not be case-insensitive.
		switch strings.ToLower(key) {
		case "protocol":
			hasProtocol = true
			if strings.ToLower(value) == string(swarm.ExternalCAProtocolCFSSL) {
				externalCA.Protocol = swarm.ExternalCAProtocolCFSSL
			} else {
				return nil, fmt.Errorf("unrecognized external CA protocol %s", value)
			}
		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
		}
	}

View on GitHub (pinned to 4f84911bfe)