{"record":{"id":"69d6f38199b12d39","repo":"fatedier/frp","slug":"cannot-add-feature-gates-after-the-feature-gate-is","errorCode":null,"errorMessage":"cannot add feature gates after the feature gate is closed","messagePattern":"cannot add feature gates after the feature gate is closed","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/policy/featuregate/feature_gate.go","lineNumber":138,"sourceCode":"\t\tif featureSpec.LockToDefault && featureSpec.Default != v {\n\t\t\treturn fmt.Errorf(\"cannot set feature gate %v to %v, feature is locked to %v\", k, v, featureSpec.Default)\n\t\t}\n\t\tenabled[k] = v\n\t}\n\n\t// Persist the changes\n\tf.known.Store(known)\n\tf.enabled.Store(enabled)\n\treturn nil\n}\n\n// Add adds features to the feature gate\nfunc (f *featureGate) Add(features map[Feature]FeatureSpec) error {\n\tf.lock.Lock()\n\tdefer f.lock.Unlock()\n\n\tif f.closed {\n\t\treturn fmt.Errorf(\"cannot add feature gates after the feature gate is closed\")\n\t}\n\n\t// Copy existing state\n\tknown := maps.Clone(f.known.Load().(map[Feature]FeatureSpec))\n\n\t// Add new features\n\tfor name, spec := range features {\n\t\tif existingSpec, found := known[name]; found {\n\t\t\tif existingSpec == spec {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"feature gate %q with different spec already exists: %v\", name, existingSpec)\n\t\t}\n\t\tknown[name] = spec\n\t}\n\n\t// Persist changes\n\tf.known.Store(known)","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/policy/featuregate/feature_gate.go#L120-L156","documentation":"Add() was called on a feature gate after Close() sealed it. Closing a gate freezes the known-feature set so later code cannot mutate feature availability; any Add after that is a programming error caught here.","triggerScenarios":"Calling featureGate.Add(features) after featureGate.Close() — typically late registration from an init path or a lazily-loaded module running after the gate was closed at startup.","commonSituations":"Plugin or module registering its feature gates in init() while main closes the gate before all imports finish; code refactor moved Add after the Close call.","solutions":["Move all Add calls before Close() in the startup sequence","Register features in init() or an explicit registration phase, then Close the gate once","If late registration is legitimate, keep the gate open (do not Close) — but prefer fixing the order"],"exampleFix":"// before\nfg.Close()\nfg.Add(map[Feature]FeatureSpec{\"Late\": {}}) // error\n\n// after\nfg.Add(map[Feature]FeatureSpec{\"Late\": {}})\nfg.Close()","handlingStrategy":"validation","validationCode":"if fg.Closed() {\n    return errors.New(\"gate closed; register features earlier in startup\")\n}","typeGuard":null,"tryCatchPattern":"if err := fg.Add(specs); err != nil && strings.Contains(err.Error(), \"after the feature gate is closed\") {\n    return fmt.Errorf(\"init-order bug: %w\", err)\n}","preventionTips":["Register all feature gates in init()/early startup; call Close exactly once, last","Add a startup assertion that gates are closed only after all modules register"],"tags":["feature-flags","lifecycle","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}