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.MapView on GitHub (pinned to 668db92c2c)
Solutions
- Check that cfg.Load() was called and returned nil before any Value()/Watch()/Get call
- Verify the exact key path against the loaded file, remembering nested YAML becomes dotted paths and keys are case-sensitive
- Add the missing key to the source, or layer a file/env source that provides it
- 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
- Always call and check cfg.Load() before any Value/Watch/Get
- Fail fast at startup on required keys instead of tolerating nil
- Keep a single source of truth for key names (constants) shared by writer and reader
- Remember Value() defers the error to the accessor call - test with .Load() != nil
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.