go-kratos/kratos · error

key not found

Error message

key not found

What it means

Sentinel error from the kratos config package (config/config.go:20). It is returned in three places: Config.Value(key) wraps it in an errValue (surfaced when you call Bool()/Int()/String()/Load() on the returned Value), Config.Watch returns it directly when the key has no value, and the generic config.Get[T] returns it when v.Load() is nil. It means the requested key does not exist in any loaded and merged config source.

Solutions

  1. Check that cfg.Load() was called and returned nil before any Value()/Watch()/Get call
  2. Verify the exact key path against the loaded file, remembering nested YAML becomes dotted paths and keys are case-sensitive
  3. Add the missing key to the source, or layer a file/env source that provides it
  4. Detect the case explicitly with errors.Is(err, config.ErrNotFound) and supply a programmatic default

Example fix

// before
v := cfg.Value("data.database.dsn")
dsn := v.String() // panics/errors with key not found at accessor time

// after
if err := cfg.Load(); err != nil { log.Fatal(err) }
dsn, err := config.Get[string](cfg, "data.database.dsn")
if errors.Is(err, config.ErrNotFound) {
    dsn = "root:root@tcp(127.0.0.1:3306)/app" // explicit default
}
Defensive patterns

Strategy: try-catch

Validate before calling

func keyExists(cfg config.Config, key string) bool {
    return cfg.Value(key).Load() != nil
}

// use before Watch:
if !keyExists(cfg, "data.database.dsn") { /* add default source first */ }

Try / catch

dsn, err := config.Get[string](cfg, "data.database.dsn")
if err != nil {
    if errors.Is(err, config.ErrNotFound) {
        // missing key: apply default or fail with a clear startup message
        dsn = defaultDSN
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: cfg.Value("data.database.dsn").String() when no source contains that dotted path; cfg.Watch("missing.key", observer) before the key exists; config.Get[string](cfg, "missing.key"). Note Value() itself never errors - the error appears on the subsequent accessor call.

Common situations: Key path typo or wrong case (config keys are case-sensitive); dotted path not matching the actual YAML/JSON nesting; cfg.Load() failed earlier and the error was ignored so no source was merged; the config file was added but the app reads it before Load; environment-specific file missing so the key only exists in some deployments.

Related errors


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/2abb6897687a48c9. Report an issue: GitHub.

Appendix: source

Thrown at config/config.go:20

import (
	"context"
	"errors"
	"reflect"
	"sync"
	"time"

	// init encoding
	_ "github.com/go-kratos/kratos/v3/encoding/json"
	_ "github.com/go-kratos/kratos/v3/encoding/proto"
	_ "github.com/go-kratos/kratos/v3/encoding/xml"
	_ "github.com/go-kratos/kratos/v3/encoding/yaml"
	"github.com/go-kratos/kratos/v3/log"
)

var _ Config = (*config)(nil)

var ErrNotFound = errors.New("key not found") // ErrNotFound is key not found.

// Observer is config observer.
type Observer func(string, Value)

// Config is a config interface.
type Config interface {
	Load() error
	Scan(v any) error
	Value(key string) Value
	Watch(key string, o Observer) error
	Close() error
}

type config struct {
	opts      options
	reader    Reader
	cached    sync.Map
	observers sync.Map

View on GitHub (pinned to 668db92c2c)