{"record":{"id":"e21c6143a6a57283","repo":"hashicorp/consul","slug":"the-indexer-must-also-implement-one-of-the-singlei","errorCode":null,"errorMessage":"The Indexer must also implement one of the SingleIndexer or MultiIndexer interfaces","messagePattern":"The Indexer must also implement one of the SingleIndexer or MultiIndexer interfaces","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/controller/cache/index/index.go","lineNumber":32,"sourceCode":"\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,\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(idx)\n\t}\n\n\treturn idx\n}\n\nfunc (i *Index) Name() string {\n\treturn i.name\n}\n","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/hashicorp/consul/blob/2397ff0d763d34f2fe37fe59fde6a7f7fc430a3e/internal/controller/cache/index/index.go#L14-L50","documentation":"index.New normalizes its Indexer argument into a MultiIndexer via a type switch: it must implement either SingleIndexer (FromArgs + FromObject) or MultiIndexer (FromArgs + FromObjects). A value implementing neither hits the default case and panics — this is a fail-fast replacement for the runtime method-not-found failure that would otherwise occur the first time the cache tried to use the index.","triggerScenarios":"Passing a type that implements only part of the required method set (e.g. FromArgs but not FromObject), or a value type whose indexer methods have pointer receivers so the method set does not satisfy the interface, or a typed-nil pointer that matches no case.","commonSituations":"Writing a custom indexer and missing one method; passing a value instead of a pointer for pointer-receiver indexers; refactoring the index interfaces while custom indexers lag behind.","solutions":["Implement the full SingleIndexer interface: FromArgs(...) ([]string, error) and FromObject(obj) ([]string, error)","Pass a pointer if your indexer methods use pointer receivers: index.New(\"name\", &myIndexer{})","Add a compile-time assertion next to the type: var _ index.SingleIndexer = (*myIndexer)(nil)"],"exampleFix":"// before: only FromArgs implemented -> panic in type switch\ntype byKind struct{}\nfunc (byKind) FromArgs(args ...any) ([]string, error) { ... }\nidx := index.New(\"kind\", byKind{})\n\n// after: full SingleIndexer interface satisfied\ntype byKind struct{}\nfunc (byKind) FromArgs(args ...any) ([]string, error) { ... }\nfunc (byKind) FromObject(r *pbresource.Resource) ([]string, error) { ... }\nvar _ index.SingleIndexer = byKind{}\nidx := index.New(\"kind\", byKind{})","handlingStrategy":"type-guard","validationCode":"// compile-time guarantee the indexer satisfies the framework interface\nvar _ index.SingleIndexer = (*MyIndexer)(nil) // fails to build if methods are missing","typeGuard":"func implementsIndexer(i any) bool {\n    switch i.(type) {\n    case index.SingleIndexer, index.MultiIndexer:\n        return true\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Copy the exact method signatures (including error returns) from the interface definition","Pass pointers for pointer-receiver indexers: &myIndexer{}","Keep a compile-time var _ assertion beside every custom indexer type"],"tags":["go","consul","controller","cache","panic","programmer-error","interfaces"],"backgroundTag":null,"analyzedSha":"2397ff0d763d34f2fe37fe59fde6a7f7fc430a3e","analyzedAt":"2026-08-15T19:19:47.700Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}