{"record":{"id":"53d5c5feb29f73c1","repo":"kubernetes/kops","slug":"error-closing-target-v","errorCode":null,"errorMessage":"error closing target: %v","messagePattern":"error closing target: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/apply_cluster.go","lineNumber":876,"sourceCode":"\n\terr = context.RunTasks(options)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error running tasks: %v\", err)\n\t}\n\n\tif !cluster.PublishesDNSRecords() {\n\t\tshouldPrecreateDNS = false\n\t}\n\n\tif shouldPrecreateDNS && clusterLifecycle != fi.LifecycleIgnore {\n\t\tif err := precreateDNS(ctx, cluster, cloud); err != nil {\n\t\t\tklog.Warningf(\"unable to pre-create DNS records - cluster startup may be slower: %v\", err)\n\t\t}\n\t}\n\n\terr = target.Finish(c.TaskMap) // This will finish the apply, and print the changes\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error closing target: %v\", err)\n\t}\n\n\tapplyResults := &ApplyResults{\n\t\tAssetBuilder: assetBuilder,\n\t}\n\n\treturn applyResults, nil\n}\n\n// upgradeSpecs ensures that fields are fully populated / defaulted\nfunc (c *ApplyClusterCmd) upgradeSpecs(ctx context.Context, assetBuilder *assets.AssetBuilder) error {\n\tfullCluster, err := PopulateClusterSpec(ctx, c.Clientset, c.Cluster, c.InstanceGroups, c.Cloud, assetBuilder)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.Cluster = fullCluster\n\n\tfor i, g := range c.InstanceGroups {","sourceCodeStart":858,"sourceCodeEnd":894,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/apply_cluster.go#L858-L894","documentation":"After tasks run and DNS pre-creation, Run calls target.Finish(c.TaskMap) to finalize the apply (write terraform files, print the dryrun changes summary, etc.). A failure there is wrapped as 'error closing target'. The apply itself mostly succeeded; the output/rendering step failed.","triggerScenarios":"target.Finish errors during `kops update cluster`: for TargetTerraform, failure writing terraform output to --out dir; for TargetDryRun, failure rendering or printing the change summary (e.g. stdout pipe closed).","commonSituations":"--out directory not writable or disk full; terraform output directory doesn't exist or is read-only; running in CI with closed stdout when using --target=dryrun; filesystem permission issues.","solutions":["Check the wrapped inner error after 'error closing target:' for the filesystem/render cause","Verify the --out directory exists and is writable (mkdir -p, check permissions)","Free disk space if the disk is full","For terraform target, run kops as a user with write access to the output dir","For dryrun in CI, ensure stdout is a valid pipe/file"],"exampleFix":"// before\nkops update cluster mycluster.example.com --target=terraform --out=/root/tf\n// after (writable directory)\nmkdir -p ./tf-out\nkops update cluster mycluster.example.com --target=terraform --out=./tf-out","handlingStrategy":"validation","validationCode":"outDir := \"./tf-out\"\nif err := os.MkdirAll(outDir, 0o755); err != nil {\n\treturn err\n}\nif f, err := os.Create(filepath.Join(outDir, \".write-test\")); err != nil {\n\treturn fmt.Errorf(\"out dir not writable: %w\", err)\n} else {\n\tf.Close(); os.Remove(f.Name())\n}","typeGuard":null,"tryCatchPattern":"if err := cmd.Run(ctx); err != nil && strings.Contains(err.Error(), \"error closing target\") {\n\t// check out-dir writability, disk space, and stdout validity\n}","preventionTips":["Always mkdir -p the --out directory and check write permissions","Ensure sufficient disk space for terraform output","In CI, keep stdout connected when using --target=dryrun","Run kops as a user with write access to the output location"],"tags":["terraform","dryrun","filesystem"],"backgroundTag":"target-output-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"}