{"record":{"id":"44af8e93a055bbf8","repo":"tailscale/tailscale","slug":"failed-to-set-up-second-tailnet-clients-w","errorCode":null,"errorMessage":"failed to set up second tailnet clients: %w","messagePattern":"failed to set up second tailnet clients: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/k8s-operator/e2e/setup.go","lineNumber":388,"sourceCode":"\t\t\treturn 0, fmt.Errorf(\"failed to create OAuth client for second tailnet: %w\", err)\n\t\t}\n\t\tsecondOperatorCreds = oauthCreds{\n\t\t\tclientID:     secondKey.ID,\n\t\t\tclientSecret: secondKey.Key,\n\t\t}\n\n\t\tsecondTSClient = tailscaleClientFromSecret(\"http://localhost:31544\", secondOperatorCreds.clientID, secondOperatorCreds.clientSecret)\n\t} else {\n\t\tif !testCAs.AppendCertsFromPEM(leStagingRootX1) {\n\t\t\treturn 0, fmt.Errorf(\"failed to parse Let's Encrypt staging root X1 cert\")\n\t\t}\n\t\ttsClient, operatorCreds, err = prodTailnetClientFromEnv(\"TS_API_CLIENT_SECRET\", \"TS_API_CLIENT_ID\", \"TS_OPERATOR_CLIENT_ID\")\n\t\tif err != nil {\n\t\t\treturn 0, fmt.Errorf(\"failed to set up first tailnet clients: %w\", err)\n\t\t}\n\t\tsecondTSClient, secondOperatorCreds, err = prodTailnetClientFromEnv(\"SECOND_TS_API_CLIENT_SECRET\", \"SECOND_TS_API_CLIENT_ID\", \"SECOND_TS_OPERATOR_CLIENT_ID\")\n\t\tif err != nil {\n\t\t\treturn 0, fmt.Errorf(\"failed to set up second tailnet clients: %w\", err)\n\t\t}\n\t}\n\n\t// Publish the trustedCAs as a ConfigMap that can be used by in-cluster\n\t// testing workloads.\n\ttestCAPEM := leStagingRootX1\n\tif *fDevcontrol {\n\t\ttestCAPEM = bytes.Join([][]byte{pebbleMiniCACert, pebbleCAChain}, []byte(\"\\n\"))\n\t}\n\tcaCM := &corev1.ConfigMap{\n\t\tObjectMeta: metav1.ObjectMeta{Name: testCAsConfigMap, Namespace: ns},\n\t\tData:       map[string]string{testCAsConfigMapKey: string(testCAPEM)},\n\t}\n\tif err := createOrUpdate(ctx, kubeClient, caCM); err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to publish test CAs ConfigMap: %w\", err)\n\t}\n\tdefer kubeClient.Delete(context.Background(), caCM)\n","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/tailscale/tailscale/blob/e2ed432399c9b0fda7aa14e9eb27784d2d893c55/cmd/k8s-operator/e2e/setup.go#L370-L406","documentation":"This error wraps failures from prodTailnetClientFromEnv when setting up API clients for the second test tailnet in the k8s-operator e2e harness. It is thrown by runTests (invoked from TestMain) when the second tailnet's credentials cannot be used to authenticate a Tailscale API client and operator credentials. It aborts the entire e2e test run before any tests execute.","triggerScenarios":"Calling prodTailnetClientFromEnv(\"SECOND_TS_API_CLIENT_SECRET\", \"SECOND_TS_API_CLIENT_ID\", \"SECOND_TS_OPERATOR_CLIENT_ID\") fails — the env vars are unset/empty or the client secret/ID pair is rejected by the Tailscale API.","commonSituations":"CI or local runs against the multi-tailnet test setup where SECOND_TS_* env vars were never exported, the second tailnet's OAuth clients were revoked or expired, or the operator client ID belongs to a different tailnet than the API client.","solutions":["Export SECOND_TS_API_CLIENT_SECRET, SECOND_TS_API_CLIENT_ID and SECOND_TS_OPERATOR_CLIENT_ID with valid values from the second tailnet's admin console","Regenerate the OAuth clients for the second tailnet if credentials were rotated or revoked","Verify all three credentials belong to the same second tailnet","Skip second-tailnet tests if the harness supports a single-tailnet mode"],"exampleFix":"// before\n$ ./run-e2e.sh\n// after\n$ export SECOND_TS_API_CLIENT_ID=k1234567890abcdef\n$ export SECOND_TS_API_CLIENT_SECRET=tskey-client-...\n$ export SECOND_TS_OPERATOR_CLIENT_ID=k0987654321fedcba\n$ ./run-e2e.sh","handlingStrategy":"validation","validationCode":"for _, k := range []string{\"SECOND_TS_API_CLIENT_SECRET\",\"SECOND_TS_API_CLIENT_ID\",\"SECOND_TS_OPERATOR_CLIENT_ID\"} {\n  if os.Getenv(k) == \"\" { return fmt.Errorf(\"missing env var %s\", k) }\n}","typeGuard":null,"tryCatchPattern":"if _, _, err := prodTailnetClientFromEnv(...); err != nil {\n  var cfgErr *MissingEnvError\n  if errors.As(err, &cfgErr) { log.Fatalf(\"set %s before running e2e\", cfgErr.Key) }\n  return err\n}","preventionTips":["Add a preflight check that fails fast listing all required SECOND_TS_* env vars","Document required env vars in the e2e README","Rotate and store credentials in a secrets manager injected into CI","Validate credentials belong to the same tailnet during setup"],"tags":["e2e","tailscale","authentication","env-vars"],"backgroundTag":"missing-env-var","analyzedSha":"e2ed432399c9b0fda7aa14e9eb27784d2d893c55","analyzedAt":"2026-09-14T12:49:01.353Z","contentChangedAt":"2026-09-14T12:49:01.353Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}