etcd-io/etcd · error
feature %q is not registered in FeatureGate %q
Error message
feature %q is not registered in FeatureGate %q
What it means
This panic is thrown by featuregate's Enabled() when the requested Feature key is present in neither the runtime 'enabled' map nor the registered 'known' specs map of that FeatureGate instance. It is an intentional fail-fast: silently returning false for an unknown gate would hide configuration mistakes. The gate's specs are populated via Add/AddMap/SetFromMap before Enabled is consulted, so an unknown key means the caller and the registration code disagree.
Source
Thrown at pkg/featuregate/feature_gate.go:342
}
// GetAll returns a copy of the map of known feature names to feature specs.
func (f *featureGate) GetAll() map[Feature]FeatureSpec {
retval := map[Feature]FeatureSpec{}
maps.Copy(retval, f.known.Load().(map[Feature]FeatureSpec))
return retval
}
// Enabled returns true if the key is enabled. If the key is not known, this call will panic.
func (f *featureGate) Enabled(key Feature) bool {
if v, ok := f.enabled.Load().(map[Feature]bool)[key]; ok {
return v
}
if v, ok := f.known.Load().(map[Feature]FeatureSpec)[key]; ok {
return v.Default
}
panic(fmt.Errorf("feature %q is not registered in FeatureGate %q", key, f.featureGateName))
}
// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
func (f *featureGate) AddFlag(fs *flag.FlagSet, flagName string) {
if flagName == "" {
flagName = defaultFlagName
}
f.lock.Lock()
// TODO(mtaufen): Shouldn't we just close it on the first Set/SetFromMap instead?
// Not all components expose a feature gates flag using this AddFlag method, and
// in the future, all components will completely stop exposing a feature gates flag,
// in favor of componentconfig.
f.closed = true
f.lock.Unlock()
known := f.KnownFeatures()
fs.Var(f, flagName, ""+
"A set of key=value pairs that describe feature gates for alpha/experimental features. "+View on GitHub (pinned to f744d457f4)
Solutions
- Find where the FeatureGate is populated (embed/config.go or wherever Add/AddMap/SetFromMap is called) and add the missing feature with featuregate.Add(FeatureName, FeatureSpec{Default: false, PreRelease: ...}) before any Enabled call.
- If the feature exists upstream, align module versions (go.mod / go.work) so the registering code and the Enabled caller come from the same etcd release; renamed/graduated gates must be referenced by their current constant.
- List what the gate actually knows with f.KnownFeatures() (or inspect the AddMap literal) to confirm the exact spelling of the key.
- If you only want an optional check, test membership of KnownFeatures() output instead of calling Enabled for possibly-absent keys.
Example fix
// before
fg := featuregate.New("my-gate")
// ... component later calls:
if fg.Enabled(featuregate.Feature("MyFeature")) { ... } // panics: never registered
// after
fg := featuregate.New("my-gate")
err := fg.Add(map[featuregate.Feature]featuregate.FeatureSpec{
"MyFeature": {Default: false, PreRelease: featuregate.Alpha},
})
if err != nil {
return err
}
// now Enabled("MyFeature") returns the default instead of panicking Defensive patterns
Strategy: validation
Validate before calling
// Before calling Enabled, confirm the feature is registered:
func featureRegistered(fg featuregate.FeatureGate, key featuregate.Feature) bool {
for _, k := range fg.KnownFeatures() { // KnownFeatures includes lock/alpha markers; check raw membership instead
if strings.TrimPrefix(k, "lock_") == string(key) || k == string(key) {
return true
}
}
return false
}
if !featureRegistered(fg, "MyFeature") {
// register it or skip the check
} Prevention
- Register all features (Add/AddMap) in one place during gate construction, before any goroutine can call Enabled.
- Reference features via typed constants, not raw strings, so renames surface as compile errors.
- Keep all etcd modules on the same release version to avoid gate-name skew.
- In tests, construct the gate through the same helper production code uses so the spec map is always populated.
When it happens
Trigger: Calling featureGate.Enabled(key) where key was never registered with featureGate.Add(...) or AddMap(...). Typical in etcd's embed server path: cfg.AddFeatureGate / options parsing register gates first, then component code calls Enabled("SomeFeature"); if the component references a feature constant introduced (or renamed) in a different version than the one that registered the gates, the panic fires.
Common situations: Version skew between etcd modules (e.g. server registered gates at v3.5 while shared code expects a v3.6 gate name); typos when passing a feature as a raw string from flags/env; calling Enabled on a freshly created gate (featuregate.New) before any Add; copy-pasting a feature name that was removed (graduated/removed features disappear from known maps after cleanup).
Related errors
- Unknown result op
- bad compare value
- bad value %v of type %T
- unsupported stm
- unexpected revision = 0. Calling SyncUpdates before SyncBase
AI-assisted analysis of etcd-io/etcd@f744d457f4 (2026-08-15).
Data as JSON: /api/errors/d33d9882d2069cb0.
Report an issue: GitHub.