derailed/k9s · error
main config.yaml load failed: %w
Error message
main config.yaml load failed: %w
What it means
Returned from Config.Load (internal/config/config.go:288) when yaml.Unmarshal of the main config file into a Config struct fails. Unlike the schema check (which only warns into the joined error), a structural unmarshal failure means the file cannot be mapped onto the config types: wrong value types (string where int expected), duplicate keys, or malformed YAML. Joined with other errors and returned after the Merge of the zero-value/previous config.
Source
Thrown at internal/config/config.go:288
// Load loads K9s configuration from file.
func (c *Config) Load(path string, force bool) error {
if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) {
if err := c.Save(force); err != nil {
return err
}
}
bb, err := os.ReadFile(path)
if err != nil {
return err
}
var errs error
if err := data.JSONValidator.Validate(json.K9sSchema, bb); err != nil {
errs = errors.Join(errs, fmt.Errorf("k9s config file %q load failed:\n%w", path, err))
}
var cfg Config
if err := yaml.Unmarshal(bb, &cfg); err != nil {
errs = errors.Join(errs, fmt.Errorf("main config.yaml load failed: %w", err))
}
c.Merge(&cfg)
return errs
}
// Save configuration to disk.
func (c *Config) Save(force bool) error {
contextName := c.K9s.ActiveContextName()
// Skip saving if no context is configured
if contextName == "" {
slog.Debug("No context configured, skipping config save")
return nil
}
clusterName, err := c.ActiveClusterName(contextName)
if err != nil {
return fmt.Errorf("unable to locate associated cluster for context %q: %w", contextName, err)
}View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Run yamllint (or python -c 'import yaml,sys; yaml.safe_load(open("config.yml"))') to get the exact line/column
- Fix the reported structural problem — usually indentation or type
- Restore from backup or delete the file so k9s regenerates defaults, then re-apply changes incrementally
Example fix
# before
k9s:
refreshRate: fast
headless: 2
# after
k9s:
refreshRate: 2
headless: false Defensive patterns
Strategy: try-catch
Validate before calling
func configParses(path string) error {
bb, err := os.ReadFile(path)
if err != nil {
return err
}
var probe map[string]any
if err := yaml.Unmarshal(bb, &probe); err != nil {
return fmt.Errorf("config.yml invalid YAML: %w", err)
}
return nil
} Type guard
func isYamlTypeError(err error) bool {
var te *yaml.TypeError
return errors.As(err, &te)
} Try / catch
if err := cfg.Load(path, false); err != nil {
var te *yaml.TypeError
if errors.As(err, &te) {
for _, msg := range te.Errors {
log.Printf("config type error: %s", msg) // line + expected type
}
// offer to regenerate the file from defaults
}
} Prevention
- Use spaces (never tabs) and a fixed 2-space indent in k9s yaml configs
- Run yamllint on config.yml inside your dotfiles CI
- Check scalar types against the docs: booleans as true/false, numbers unquoted
When it happens
Trigger: Hand-editing config.yml and introducing a tab character, wrong indentation level, or a scalar of the wrong type (e.g. refreshRate: fast instead of an integer); YAML aliases/anchors the decoder rejects; file truncated mid-write.
Common situations: Manual edits late at night; editor inserting tabs; syncing dotfiles that got mangled by templating; disk-full partial writes.
Related errors
- context-config yaml load failed: %w %s
- plugin unmarshal failed for %s: %w
- fail to load rawConfig: %w
- k9s config file %q load failed: %w
- duplicate input name %q
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/cdb16dfb93c07ebc.
Report an issue: GitHub.