{"record":{"id":"c235d5f3108d3273","repo":"kubernetes/kops","slug":"error-writing-cluster-v","errorCode":null,"errorMessage":"error writing Cluster: %v","messagePattern":"error writing Cluster: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/client/simple/vfsclientset/cluster.go","lineNumber":154,"sourceCode":"\t}\n\n\tif old == nil {\n\t\treturn nil, errors.NewNotFound(schema.GroupResource{Group: api.GroupName, Resource: \"Cluster\"}, clusterName)\n\t}\n\n\tif err := validation.ValidateClusterUpdate(c, status, old, r.vfsContext).ToAggregate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !apiequality.Semantic.DeepEqual(old.Spec, c.Spec) {\n\t\tc.SetGeneration(old.GetGeneration() + 1)\n\t}\n\n\tif err := r.writeConfig(ctx, c, r.basePath.Join(clusterName, registry.PathCluster), c, vfs.WriteOptionOnlyIfExists); err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, fmt.Errorf(\"error writing Cluster: %v\", err)\n\t}\n\n\treturn c, nil\n}\n\n// List returns a slice containing all the cluster names\n// It skips directories that don't look like clusters\nfunc (r *ClusterVFS) listNames(ctx context.Context) ([]string, error) {\n\tpaths, err := r.basePath.ReadTree(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error reading state store: %v\", err)\n\t}\n\n\tvar keys []string\n\tfor _, p := range paths {\n\t\trelativePath, err := vfs.RelativePath(r.basePath, p)\n\t\tif err != nil {\n\t\t\treturn nil, err","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/client/simple/vfsclientset/cluster.go#L136-L172","documentation":"ClusterVFS::Update writes the cluster config with vfs.WriteOptionOnlyIfExists, i.e. it only overwrites an existing cluster record. This error wraps the underlying VFS write failure that is not an IsNotExist condition (those are returned unwrapped).","triggerScenarios":"Calling Update on a cluster whose config write fails for reasons other than the file not existing: backend auth failure, permissions, network error to the object store, or serialization/write errors inside writeConfig.","commonSituations":"Expired cloud credentials during kops update/replace; bucket policies denying put-object; transient network failures to s3/gcs; concurrent modifications conflicting with the OnlyIfExists guard.","solutions":["Inspect the wrapped cause for the real backend error (auth, permission, network)","Confirm the cluster exists first — if not, Update should return the IsNotExist path instead","Retry after restoring credentials/connectivity to the state store","Re-run kops update cluster --yes once the backend is reachable"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure the cluster exists before Update\nexisting, err := clusterVFS.Get(ctx, cluster.Name, metav1.GetOptions{})\nif err != nil || existing == nil {\n\treturn fmt.Errorf(\"cluster %q not found; Update requires an existing cluster\", cluster.Name)\n}","typeGuard":null,"tryCatchPattern":"updated, err := clusterVFS.Update(cluster, status)\nif err != nil && strings.Contains(err.Error(), \"error writing Cluster\") {\n\tif os.IsNotExist(err) {\n\t\treturn ErrClusterNotFound\n\t}\n\t// transient backend failure — retry with backoff\n\terr = retry.Do(func() error { _, err = clusterVFS.Update(cluster, status); return err })\n}","preventionTips":["Confirm credentials/connectivity to the object store before updates","Handle os.IsNotExist separately (cluster missing) from wrapped write errors","Avoid concurrent writers to the same cluster state","Retry transient network errors with backoff"],"tags":["go","vfs","storage","io"],"backgroundTag":"state-store-write-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}