{"record":{"id":"49167b3f26ac3e49","repo":"argoproj/argo-workflows","slug":"client-certificate-and-client-key-must-be-prov","errorCode":null,"errorMessage":"--client-certificate and --client-key must be provided together","messagePattern":"--client-certificate and --client-key must be provided together","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/argo/commands/client/conn.go","lineNumber":66,"sourceCode":"\tcmd.PersistentFlags().StringVar(&instanceID, \"instanceid\", os.Getenv(\"ARGO_INSTANCEID\"), \"submit with a specific controller's instance id label. Default to the ARGO_INSTANCEID environment variable.\")\n\t// \"-s\" like kubectl\n\tcmd.PersistentFlags().StringVarP(&ArgoServerOpts.URL, \"argo-server\", \"s\", os.Getenv(\"ARGO_SERVER\"), \"API server `host:port`. e.g. localhost:2746. Defaults to the ARGO_SERVER environment variable.\")\n\tcmd.PersistentFlags().StringVar(&ArgoServerOpts.Path, \"argo-base-href\", os.Getenv(\"ARGO_BASE_HREF\"), \"Path to use with HTTP client due to Base HREF. Defaults to the ARGO_BASE_HREF environment variable.\")\n\tcmd.PersistentFlags().BoolVar(&ArgoServerOpts.HTTP1, \"argo-http1\", os.Getenv(\"ARGO_HTTP1\") == \"true\", \"If true, use the HTTP client. Defaults to the ARGO_HTTP1 environment variable.\")\n\tcmd.PersistentFlags().StringSliceVarP(&ArgoServerOpts.Headers, \"header\", \"H\", []string{}, \"Sets additional header to all requests made by Argo CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers) Used only when either ARGO_HTTP1 or --argo-http1 is set to true.\")\n\t// \"-e\" for encrypted - like zip\n\tcmd.PersistentFlags().BoolVarP(&ArgoServerOpts.Secure, \"secure\", \"e\", os.Getenv(\"ARGO_SECURE\") != \"false\", \"Whether or not the server is using TLS with the Argo Server. Defaults to the ARGO_SECURE environment variable.\")\n\t// \"-k\" like curl\n\tcmd.PersistentFlags().BoolVarP(&ArgoServerOpts.InsecureSkipVerify, \"insecure-skip-verify\", \"k\", os.Getenv(\"ARGO_INSECURE_SKIP_VERIFY\") == \"true\", \"If true, the Argo Server's certificate will not be checked for validity. This will make your HTTPS connections insecure. Defaults to the ARGO_INSECURE_SKIP_VERIFY environment variable.\")\n}\n\nfunc NewAPIClient(ctx context.Context) (context.Context, apiclient.Client, error) {\n\t// Reuse the explicit kubectl client certificate flags in server mode.\n\tArgoServerOpts.ClientCert = overrides.AuthInfo.ClientCertificate\n\tArgoServerOpts.ClientKey = overrides.AuthInfo.ClientKey\n\tArgoServerOpts.CACert = overrides.ClusterInfo.CertificateAuthority\n\tif (ArgoServerOpts.ClientCert == \"\") != (ArgoServerOpts.ClientKey == \"\") {\n\t\treturn nil, nil, errors.New(\"--client-certificate and --client-key must be provided together\")\n\t}\n\n\tvar proxy func(*http.Request) (*url.URL, error)\n\tif overrides.ClusterInfo.ProxyURL != \"\" {\n\t\tproxyURL, err := url.Parse(overrides.ClusterInfo.ProxyURL)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tproxy = http.ProxyURL(proxyURL)\n\t}\n\treturn apiclient.NewClientFromOptsWithContext(ctx,\n\t\tapiclient.Opts{\n\t\t\tArgoServerOpts: ArgoServerOpts,\n\t\t\tInstanceID:     instanceID,\n\t\t\tAuthSupplier: func() string {\n\t\t\t\tauthString, err := GetAuthString(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlogger := logging.RequireLoggerFromContext(ctx)","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/cmd/argo/commands/client/conn.go#L48-L84","documentation":"NewAPIClient validates kubectl-style auth overrides before dialing the argo server. mTLS requires both a client certificate and its matching private key; supplying only one is guaranteed misconfiguration, so it fails fast with this error.","triggerScenarios":"Setting --client-certificate without --client-key (or vice versa) on any argo CLI command, or having only one of client-certificate-data/client-key-data in the kubeconfig user entry.","commonSituations":"Copying kubectl config examples that only set the cert; rotating certificates and replacing only one file; shell scripts where one flag is conditionally added.","solutions":["Pass both flags together: --client-certificate /path/tls.crt --client-key /path/tls.key.","Fix the kubeconfig user entry so client-certificate-data and client-key-data are both present.","If the server doesn't require client certs, remove both flags and authenticate another way (token, SSO)."],"exampleFix":"// before\nargo list --client-certificate tls.crt\n// after\nargo list --client-certificate tls.crt --client-key tls.key","handlingStrategy":"validation","validationCode":"if [ -n \"$CLIENT_CERT\" ] || [ -n \"$CLIENT_KEY\" ]; then\n  [ -n \"$CLIENT_CERT\" ] && [ -n \"$CLIENT_KEY\" ] || { echo 'need both cert and key'; exit 1; }\nfi","typeGuard":null,"tryCatchPattern":"if ! argo list --client-certificate \"$c\" --client-key \"$k\" 2>err.txt; then\n  grep -q 'must be provided together' err.txt && echo 'fix mTLS flags'\nfi","preventionTips":["Always pass --client-certificate and --client-key as a pair.","Keep both files in the same directory and rotate them together.","Validate the kubeconfig user entry has both cert and key data."],"tags":["cli","tls","authentication"],"backgroundTag":"incomplete-mtls-configuration","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}