{"id":"f74fc5e1a6ed0b82","repo":"docker/cli","slug":"the-external-ca-option-needs-a-url-parameter","errorCode":null,"errorMessage":"the external-ca option needs a url= parameter","messagePattern":"the external-ca option needs a url= parameter","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command/swarm/opts.go","lineNumber":213,"sourceCode":"\t\tcase \"cacert\":\n\t\t\tcacontents, err := os.ReadFile(value)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"unable to read CA cert for external CA: %w\", err)\n\t\t\t}\n\t\t\tif pemBlock, _ := pem.Decode(cacontents); pemBlock == nil {\n\t\t\t\treturn nil, errors.New(\"CA cert for external CA must be in PEM format\")\n\t\t\t}\n\t\t\texternalCA.CACert = string(cacontents)\n\t\tdefault:\n\t\t\texternalCA.Options[key] = value\n\t\t}\n\t}\n\n\tif !hasProtocol {\n\t\treturn nil, errors.New(\"the external-ca option needs a protocol= parameter\")\n\t}\n\tif !hasURL {\n\t\treturn nil, errors.New(\"the external-ca option needs a url= parameter\")\n\t}\n\n\treturn &externalCA, nil\n}\n\nfunc addSwarmCAFlags(flags *pflag.FlagSet, options *swarmCAOptions) {\n\tflags.DurationVar(&options.nodeCertExpiry, flagCertExpiry, 90*24*time.Hour, \"Validity period for node certificates (ns|us|ms|s|m|h)\")\n\tflags.Var(&options.externalCA, flagExternalCA, \"Specifications of one or more certificate signing endpoints\")\n}\n\nfunc addSwarmFlags(flags *pflag.FlagSet, options *swarmOptions) {\n\tflags.Int64Var(&options.taskHistoryLimit, flagTaskHistoryLimit, 5, \"Task history retention limit\")\n\tflags.DurationVar(&options.dispatcherHeartbeat, flagDispatcherHeartbeat, 5*time.Second, \"Dispatcher heartbeat period (ns|us|ms|s|m|h)\")\n\tflags.Uint64Var(&options.maxSnapshots, flagMaxSnapshots, 0, \"Number of additional Raft snapshots to retain\")\n\tflags.SetAnnotation(flagMaxSnapshots, \"version\", []string{\"1.25\"})\n\tflags.Uint64Var(&options.snapshotInterval, flagSnapshotInterval, 10000, \"Number of log entries between Raft snapshots\")\n\tflags.SetAnnotation(flagSnapshotInterval, \"version\", []string{\"1.25\"})\n\taddSwarmCAFlags(flags, &options.swarmCAOptions)","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/docker/cli/blob/e9452d6e785f6e365712b9d71bd7517591773c86/cli/command/swarm/opts.go#L195-L231","documentation":"Returned by parseExternalCA when an `--external-ca` specification lacks a `url=` field. The URL identifies the external CA signing endpoint the swarm managers forward certificate signing requests to, so a spec without it is unusable.","triggerScenarios":"`docker swarm init` or `docker swarm update` with `--external-ca protocol=cfssl` (or any spec omitting url), or a spec where shell quoting/comma handling dropped the url field before it reached the flag parser.","commonSituations":"Unquoted specs split by the shell; automation templates with an empty URL variable; assuming the URL can be provided via a separate flag.","solutions":["Include the endpoint: `--external-ca protocol=cfssl,url=https://ca.example.com`.","Quote the full spec to survive shell parsing.","Verify templated variables expand to a non-empty URL before invoking the CLI."],"exampleFix":"# before\ndocker swarm update --external-ca protocol=cfssl\n# after\ndocker swarm update --external-ca \"protocol=cfssl,url=https://ca.example.com\"","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":["docker","swarm","external-ca","cli-flags","validation"],"analyzedSha":"e9452d6e785f6e365712b9d71bd7517591773c86","analyzedAt":"2026-08-01T07:09:22.474Z","schemaVersion":2}