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.

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)

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.