hashicorp/consul · error

The Indexer must also implement one of the SingleIndexer or

Error message

The Indexer must also implement one of the SingleIndexer or MultiIndexer interfaces

What it means

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.

Source

Thrown at internal/controller/cache/index/index.go:32

	indexer  MultiIndexer
}

func New(name string, i Indexer, opts ...IndexOption) *Index {
	if name == "" {
		panic("all indexers must have a non-empty name")
	}
	if i == nil {
		panic("no indexer was supplied when creating a new cache Index")
	}

	var multiIndexer MultiIndexer
	switch v := i.(type) {
	case SingleIndexer:
		multiIndexer = singleIndexWrapper{indexer: v}
	case MultiIndexer:
		multiIndexer = v
	default:
		panic("The Indexer must also implement one of the SingleIndexer or MultiIndexer interfaces")
	}

	idx := &Index{
		name:    name,
		indexer: multiIndexer,
	}

	for _, opt := range opts {
		opt(idx)
	}

	return idx
}

func (i *Index) Name() string {
	return i.name
}

View on GitHub (pinned to 2397ff0d76)

Solutions

  1. Implement the full SingleIndexer interface: FromArgs(...) ([]string, error) and FromObject(obj) ([]string, error)
  2. Pass a pointer if your indexer methods use pointer receivers: index.New("name", &myIndexer{})
  3. Add a compile-time assertion next to the type: var _ index.SingleIndexer = (*myIndexer)(nil)

Example fix

// before: only FromArgs implemented -> panic in type switch
type byKind struct{}
func (byKind) FromArgs(args ...any) ([]string, error) { ... }
idx := index.New("kind", byKind{})

// after: full SingleIndexer interface satisfied
type byKind struct{}
func (byKind) FromArgs(args ...any) ([]string, error) { ... }
func (byKind) FromObject(r *pbresource.Resource) ([]string, error) { ... }
var _ index.SingleIndexer = byKind{}
idx := index.New("kind", byKind{})
Defensive patterns

Strategy: type-guard

Validate before calling

// compile-time guarantee the indexer satisfies the framework interface
var _ index.SingleIndexer = (*MyIndexer)(nil) // fails to build if methods are missing

Type guard

func implementsIndexer(i any) bool {
    switch i.(type) {
    case index.SingleIndexer, index.MultiIndexer:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hashicorp/consul@2397ff0d76 (2026-08-15). Data as JSON: /api/errors/e21c6143a6a57283. Report an issue: GitHub.