{"record":{"id":"1530984184fea9fa","repo":"hashicorp/consul","slug":"all-indexers-must-have-a-non-empty-name","errorCode":null,"errorMessage":"all indexers must have a non-empty name","messagePattern":"all indexers must have a non-empty name","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/controller/cache/index/index.go","lineNumber":19,"sourceCode":"// Copyright IBM Corp. 2024, 2026\n// SPDX-License-Identifier: BUSL-1.1\n\npackage index\n\nimport (\n\t\"github.com/hashicorp/consul/proto-public/pbresource\"\n\tiradix \"github.com/hashicorp/go-immutable-radix/v2\"\n)\n\ntype Index struct {\n\tname     string\n\trequired bool\n\tindexer  MultiIndexer\n}\n\nfunc New(name string, i Indexer, opts ...IndexOption) *Index {\n\tif name == \"\" {\n\t\tpanic(\"all indexers must have a non-empty name\")\n\t}\n\tif i == nil {\n\t\tpanic(\"no indexer was supplied when creating a new cache Index\")\n\t}\n\n\tvar multiIndexer MultiIndexer\n\tswitch v := i.(type) {\n\tcase SingleIndexer:\n\t\tmultiIndexer = singleIndexWrapper{indexer: v}\n\tcase MultiIndexer:\n\t\tmultiIndexer = v\n\tdefault:\n\t\tpanic(\"The Indexer must also implement one of the SingleIndexer or MultiIndexer interfaces\")\n\t}\n\n\tidx := &Index{\n\t\tname:    name,\n\t\tindexer: multiIndexer,","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/hashicorp/consul/blob/2397ff0d763d34f2fe37fe59fde6a7f7fc430a3e/internal/controller/cache/index/index.go#L1-L37","documentation":"index.New builds a cache index for Consul's resource controller framework. The index name is mandatory: it becomes the key used to register and later query the index in the controller cache. An empty name is a programming error rejected immediately with a panic at construction time, so the failure surfaces during controller setup rather than as a silent cache miss later.","triggerScenarios":"Calling index.New(\"\", indexer) — usually a name built dynamically from a variable or config field that was never set, a loop variable that ended up empty, or a refactoring that dropped the literal.","commonSituations":"Generating index definitions from external config where the name field is optional; refactoring index registration code; copy-pasting an index definition and forgetting to change the name.","solutions":["Pass a non-empty, descriptive, unique name, e.g. index.New(\"kind\", indexer)","If names are generated, default them when empty: if name == \"\" { name = kind + \"-default\" }","Add a startup test asserting every index handed to WithWatch has a non-empty name"],"exampleFix":"// before\nidx := index.New(\"\", indexer) // panic: all indexers must have a non-empty name\n\n// after\nidx := index.New(\"kind\", indexer)","handlingStrategy":"validation","validationCode":"// validate index definitions before controller construction\nfunc assertIndexNames(idxs []*index.Index) error {\n    for _, i := range idxs {\n        if i == nil || i.String() == \"\" { // i.String() returns the name\n            return fmt.Errorf(\"all indexes must have non-empty names\")\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use string constants for index names shared between registration and query code","When generating indexes from config, default empty names at load time","Add a unit test over the full index list handed to WithWatch"],"tags":["go","consul","controller","cache","panic","programmer-error"],"backgroundTag":null,"analyzedSha":"2397ff0d763d34f2fe37fe59fde6a7f7fc430a3e","analyzedAt":"2026-08-15T19:19:47.700Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}