docker/cli · error

the external-ca option needs a url= parameter

Error message

the external-ca option needs a url= parameter

What it means

Thrown by parseExternalCA (cli/command/swarm/opts.go:213) when an `--external-ca` specification contains no `url=` key. The external CA endpoint URL is mandatory; if hasURL is false after the field loop, parsing aborts.

Solutions

  1. Add `url=<endpoint>` to the spec: `--external-ca protocol=cfssl,url=https://ca.example.com`.
  2. Validate the spec string contains `url=` before invoking the command.

Example fix

// before
docker swarm update --external-ca protocol=cfssl

// after
docker swarm update --external-ca protocol=cfssl,url=https://ca.example.com
Defensive patterns

Strategy: validation

Validate before calling

// Require url= in the external-ca spec string.
if !strings.Contains(spec, "url=") {
	return errors.New("external-ca spec needs url=")
}

Prevention

When it happens

Trigger: Passing `--external-ca protocol=cfssl` (url omitted) or any spec missing the `url=` key.

Common situations: Forgot the url key; templating dropped it; copy-paste left only the protocol.

Related errors


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

Appendix: source

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

		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"})
	flags.Uint64Var(&options.snapshotInterval, flagSnapshotInterval, 10000, "Number of log entries between Raft snapshots")
	flags.SetAnnotation(flagSnapshotInterval, "version", []string{"1.25"})
	addSwarmCAFlags(flags, &options.swarmCAOptions)

View on GitHub (pinned to 4f84911bfe)