{"record":{"id":"d2c69d322655ad04","repo":"tailscale/tailscale","slug":"invalid-kubeconfig","errorCode":null,"errorMessage":"invalid kubeconfig","messagePattern":"invalid kubeconfig","errorType":"validation","errorClass":"errInvalidKubeconfig","httpStatus":null,"severity":"error","filePath":"cmd/tailscale/cli/configure-kube.go","lineNumber":215,"sourceCode":"}\n\n// appendOrSetNamed finds a map with a \"name\" key matching name in dst, and\n// replaces it with val. If no such map is found, val is appended to dst.\nfunc appendOrSetNamed(dst []any, name string, val map[string]any) []any {\n\tif got := slices.IndexFunc(dst, func(m any) bool {\n\t\tif m, ok := m.(map[string]any); ok {\n\t\t\treturn m[\"name\"] == name\n\t\t}\n\t\treturn false\n\t}); got != -1 {\n\t\tdst[got] = val\n\t} else {\n\t\tdst = append(dst, val)\n\t}\n\treturn dst\n}\n\nvar errInvalidKubeconfig = errors.New(\"invalid kubeconfig\")\n\nfunc updateKubeconfig(cfgYaml []byte, scheme, fqdn string) ([]byte, error) {\n\tvar cfg map[string]any\n\tif len(cfgYaml) > 0 {\n\t\tif err := yaml.Unmarshal(cfgYaml, &cfg); err != nil {\n\t\t\treturn nil, errInvalidKubeconfig\n\t\t}\n\t}\n\tif cfg == nil {\n\t\tcfg = map[string]any{\n\t\t\t\"apiVersion\": \"v1\",\n\t\t\t\"kind\":       \"Config\",\n\t\t}\n\t} else if cfg[\"apiVersion\"] != \"v1\" || cfg[\"kind\"] != \"Config\" {\n\t\treturn nil, errInvalidKubeconfig\n\t}\n\n\tvar clusters []any","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/tailscale/cli/configure-kube.go#L197-L233","documentation":"updateKubeconfig unmarshals your existing kubeconfig YAML with yaml.Unmarshal; if parsing fails it returns the sentinel errInvalidKubeconfig. The command rewrites kubeconfig entries to point at the tailnet FQDN, so it must first load the current file found via kubeconfigPath() (honoring KUBECONFIG). Note the sentinel discards the yaml error detail, so the exact syntax problem is not shown.","triggerScenarios":"The file that kubeconfigPath() resolves (usually $KUBECONFIG or ~/.kube/config) exists but is not valid YAML — tabs used for indentation, truncated file, merge of conflicting snippets, or binary/garbage content.","commonSituations":"Hand-edited kubeconfig with a typo; multiple KUBECONFIG entries where one file is corrupt; a partially-written file after an interrupted kubectl operation; templating artifacts left in the YAML.","solutions":["Validate the file: `yq -e '.' \"$KUBECONFIG\"` or `kubectl config view --raw` to locate the syntax error","Fix or remove the malformed file, keeping a backup first","Point KUBECONFIG at a known-good file, or unset it so the default path is used, then re-run `tailscale configure kube`"],"exampleFix":"# before\n$ tailscale configure kube apiserver\nerror: invalid kubeconfig\n\n# after (find and fix the YAML, then retry)\n$ yq -e '.' \"$HOME/.kube/config\"   # reports the exact parse problem\n$ cp ~/.kube/config ~/.kube/config.bak\n$ tailscale configure kube apiserver","handlingStrategy":"validation","validationCode":"KUBECONFIG=\"${KUBECONFIG:-$HOME/.kube/config}\"\nif [ -s \"$KUBECONFIG\" ] && ! yq -e '.' \"$KUBECONFIG\" >/dev/null 2>&1; then\n  echo \"invalid YAML in $KUBECONFIG; fix or back it up first\"; exit 1\nfi\ntailscale configure kube \"$APISERVER\"","typeGuard":null,"tryCatchPattern":"var cfg map[string]any\nif err := yaml.Unmarshal(cfgYaml, &cfg); err != nil {\n    return fmt.Errorf(\"kubeconfig at %s is malformed: %w\", path, err) // keep parse detail\n}","preventionTips":["Lint kubeconfig files with yq/kubectl config view after every manual edit","Back up kubeconfig before letting tools rewrite it","Avoid tabs in kubeconfig YAML; use spaces"],"tags":["kubeconfig","yaml","cli","kubernetes"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}