ahmetb/kubectx · error

failed to close encoder for file %d: %w

Error message

failed to close encoder for file %d: %w

What it means

internal/kubeconfig.Kubeconfig.Save writes every tracked kubeconfig file through a yaml.Encoder; after encoding it calls enc.Close() to flush buffered output. This error wraps any failure from that Close call, meaning the YAML document was encoded in memory but the bytes may not have been fully flushed to the underlying file. The wrapped error (%w) carries the actual I/O cause (usually from the underlying os.File flush path).

Source

Thrown at internal/kubeconfig/kubeconfig.go:214

	seqNode := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
	for _, elem := range elements {
		seqNode.Content = append(seqNode.Content, elem.YNode())
	}
	return yaml.NewRNode(seqNode), nil
}

func (k *Kubeconfig) Save() error {
	for i := range k.files {
		if err := k.files[i].f.Reset(); err != nil {
			return fmt.Errorf("failed to reset file %d: %w", i, err)
		}
		enc := yaml.NewEncoder(k.files[i].f)
		enc.SetIndent(0)
		if err := enc.Encode(k.files[i].config.YNode()); err != nil {
			return fmt.Errorf("failed to encode file %d: %w", i, err)
		}
		if err := enc.Close(); err != nil {
			return fmt.Errorf("failed to close encoder for file %d: %w", i, err)
		}
	}
	return nil
}

View on GitHub (pinned to 12ad6fb22e)

Solutions

  1. Check the wrapped error for the root cause (disk full, EROFS, EIO) and free space / fix filesystem access
  2. Verify no other goroutine or code path closes the same file before Save is called
  3. Check write permissions on the kubeconfig file and parent directory
  4. Re-run Save after fixing the environment; the error is transient unless the filesystem is broken

Example fix

// before
if err := k.files[i].f.Close(); err != nil { log.Println(err) }
k.Save()
// after
if err := k.Save(); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) {
        log.Fatalf("save failed on %s: %v", pe.Path, pe.Err)
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go
// Before Save, ensure the target files are writable and space exists
for _, p := range paths {
    fi, err := os.Stat(p)
    if err != nil || fi.Mode().Perm()&0200 == 0 {
        return fmt.Errorf("kubeconfig %s not writable", p)
    }
}

Type guard

func asPathError(err error) (*fs.PathError, bool) {
    var pe *fs.PathError
    if errors.As(err, &pe) { return pe, true }
    return nil, false
}

Try / catch

if err := k.Save(); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) {
        log.Printf("save failed (%s): %v", pe.Path, pe.Err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Save (directly or via switchNamespace/clearNamespace) when the yaml.Encoder's Close fails, typically because the underlying os.File write errors at flush time: disk full, file closed elsewhere, permission change mid-write, or I/O error on the output stream.

Common situations: Disk quota exceeded while saving a modified ~/.kube/config; read-only filesystem remounted after the file was opened; an underlying file that was closed by another code path before Save; NFS/network filesystem transient errors.

Related errors


AI-assisted analysis of ahmetb/kubectx@12ad6fb22e (2026-09-02). Data as JSON: /api/errors/f954f473239c815f. Report an issue: GitHub.