hashicorp/consul · error

a predefined cache query with name %q already exists

Error message

a predefined cache query with name %q already exists

What it means

Controller.WithQuery registers a named cache query that reconcilers and dependency mappers can look up by string during execution. Query names form the lookup namespace; registering the same name twice panics because the second registration would silently shadow the first and change behavior depending on registration order.

Source

Thrown at internal/controller/controller.go:133

		panic(fmt.Sprintf("resource type %q already has a configured watch", key))
	}

	w := newWatch(watchedType, mapper)

	for _, idx := range indexes {
		w.addIndex(idx)
	}

	ctl.watches[key] = w

	return ctl
}

// WithQuery will add a named query to the controllers cache for usage during reconcile or in dependency mappers
func (ctl *Controller) WithQuery(queryName string, fn cache.Query) *Controller {
	_, duplicate := ctl.queries[queryName]
	if duplicate {
		panic(fmt.Sprintf("a predefined cache query with name %q already exists", queryName))
	}

	ctl.queries[queryName] = fn
	return ctl
}

// WithCustomWatch adds a new custom watch. Custom watches do not affect the controller cache.
func (ctl *Controller) WithCustomWatch(source *Source, mapper CustomDependencyMapper) *Controller {
	if source == nil {
		panic("source must not be nil")
	}

	if mapper == nil {
		panic("mapper must not be nil")
	}

	ctl.customWatches = append(ctl.customWatches, customWatch{source, mapper})
	return ctl

View on GitHub (pinned to 2397ff0d76)

Solutions

  1. Give each query a unique name (constants help: const QueryByKind = "by-kind")
  2. Consolidate registration of shared queries into a single helper called once
  3. When registering from multiple modules, keep a set of used names and skip or error on duplicates before calling WithQuery

Example fix

// before
ctl.WithQuery("by-name", byName)
ctl.WithQuery("by-name", byNameV2) // panic: already exists

// after
ctl.WithQuery("by-name", byName)
ctl.WithQuery("by-name-v2", byNameV2)
Defensive patterns

Strategy: validation

Validate before calling

// deduplicate query names before registering
names := map[string]bool{}
for name, fn := range queries {
    if names[name] {
        return fmt.Errorf("duplicate cache query name %q", name)
    }
    names[name] = true
    ctl.WithQuery(name, fn)
}

Prevention

When it happens

Trigger: Calling WithQuery("name", fn) twice with the same name on one Controller — typically copy-pasted registration blocks, two modules independently registering a common helper query, or refactored setup code that double-registers.

Common situations: Splitting controller wiring across files that both register a shared query; merging controller setups; renaming a query to a name already in use.

Related errors


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