abiosoft/colima · error

error encoding YAML: %w

Error message

error encoding YAML: %w

What it means

WriteYAML (util/yamlutil/yaml.go:20) marshals an arbitrary Go value with yaml.v3 and wraps a Marshal failure. yaml.v3 only fails to marshal values Go cannot represent in YAML: channels, functions, cyclic data structures, or types whose custom MarshalYAML returns an error. colima's own config structs never contain such types, so this error almost always comes from third-party code reusing WriteYAML with a richer value.

Source

Thrown at util/yamlutil/yaml.go:20

import (
	"bytes"
	"fmt"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/abiosoft/colima/config"
	"github.com/abiosoft/colima/embedded"
	"gopkg.in/yaml.v3"
)

// WriteYAML encodes struct to file as YAML.
func WriteYAML(value any, file string) error {
	b, err := yaml.Marshal(value)
	if err != nil {
		return fmt.Errorf("error encoding YAML: %w", err)
	}

	return os.WriteFile(file, b, 0644)
}

// Save saves the config.
func Save(c config.Config, file string) error {
	b, err := encodeYAML(c)
	if err != nil {
		return err
	}
	if err := os.WriteFile(file, b, 0644); err != nil {
		return fmt.Errorf("error writing yaml file: %w", err)
	}

	return nil
}

View on GitHub (pinned to c3a5f9184d)

Solutions

  1. Marshal a dedicated DTO that excludes chan/func fields (loggers, cancels, locks)
  2. Implement yaml.Marshaler on the offending type or add `yaml:"-"` tags to skip those fields
  3. Break cycles by omitting back-references before serializing
  4. Log the wrapped cause — it names the exact type yaml choked on

Example fix

// before
type state struct {
    Name string
    Done chan struct{} // yaml.Marshal fails
}
util.WriteYAML(state{...}, "s.yaml")

// after
type stateDTO struct {
    Name string `yaml:"name"`
}
util.WriteYAML(stateDTO{Name: s.Name}, "s.yaml")
Defensive patterns

Strategy: validation

Validate before calling

// reject values containing chan/func fields before marshaling
func marshalable(v any) bool {
    t := reflect.TypeOf(v)
    for t != nil && (t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice || t.Kind() == reflect.Array || t.Kind() == reflect.Map) {
        t = t.Elem()
    }
    if t == nil { return true }
    for i := 0; i < t.NumField(); i++ {
        switch t.Field(i).Type.Kind() {
        case reflect.Chan, reflect.Func, reflect.UnsafePointer:
            return false
        }
    }
    return true
}

Type guard

func isTypeError(err error) bool {
    s := err.Error()
    return strings.Contains(s, "cannot marshal type") || strings.Contains(s, "unsupported type")
}

Try / catch

if err := util.WriteYAML(value, path); err != nil {
    if isTypeError(err) {
        // strip chan/func fields into a DTO and write that instead
        return writeDTO(value, path)
    }
    return fmt.Errorf("persisting %s: %w", path, err)
}

Prevention

When it happens

Trigger: Calling util.WriteYAML(value, file) where value embeds a chan or func field, a self-referencing pointer graph, or a type with a broken MarshalYAML method.

Common situations: Embedding colima as a library in other tooling and persisting runtime structs; copying a struct that gained a func/chan field during refactoring; custom types with constructors injecting loggers/cancellations into otherwise-serializable structs.

Related errors


AI-assisted analysis of abiosoft/colima@c3a5f9184d (2026-08-15). Data as JSON: /api/errors/85667a19be43b7a7. Report an issue: GitHub.