grpc/grpc-java · error · IllegalStateException
Configurators are already set
Error message
Configurators are already set
What it means
ConfiguratorRegistry.setConfigurators() is a write-once API: it stores an unmodifiable snapshot of the configurators and flips a wasConfiguratorsSet flag. Any second call — even with an identical or empty list — throws IllegalStateException because reconfiguration after initialization would violate the registry's invariants.
Source
Thrown at api/src/main/java/io/grpc/ConfiguratorRegistry.java:60
/**
* Returns the default global instance of the configurator registry.
*/
public static synchronized ConfiguratorRegistry getDefaultRegistry() {
if (instance == null) {
instance = new ConfiguratorRegistry();
}
return instance;
}
/**
* Sets the configurators in this registry. This method can only be called once.
*
* @param configurators the configurators to set
* @throws IllegalStateException if this method is called more than once
*/
public synchronized void setConfigurators(List<? extends Configurator> configurators) {
if (wasConfiguratorsSet) {
throw new IllegalStateException("Configurators are already set");
}
this.configurators = Collections.unmodifiableList(new ArrayList<>(configurators));
wasConfiguratorsSet = true;
}
/**
* Returns a list of the configurators in this registry.
*/
public synchronized List<Configurator> getConfigurators() {
if (!wasConfiguratorsSet) {
configuratorsCallCountBeforeSet++;
}
return configurators;
}
/**
* Returns the number of times getConfigurators() was called before
* setConfigurators() was successfully invoked.View on GitHub (pinned to 64daddc1f3)
Solutions
- Call setConfigurators() exactly once per registry instance, before any read/use
- Wrap the call in an idempotency check or set a local boolean so retries don't re-invoke it
- If configurators must change, create a new registry instance instead of reconfiguring the existing one
Example fix
// before
registry.setConfigurators(configurators); // may run again on reload
// after
if (!initialized) {
registry.setConfigurators(configurators);
initialized = true;
} Defensive patterns
Strategy: type-guard
Type guard
boolean canSetConfigurators(ConfiguratorRegistry registry) {
try {
java.lang.reflect.Field f = ConfiguratorRegistry.class.getDeclaredField("wasConfiguratorsSet");
f.setAccessible(true);
return !f.getBoolean(registry);
} catch (ReflectiveOperationException e) {
return false;
}
} Try / catch
try {
registry.setConfigurators(configurators);
} catch (IllegalStateException e) {
if ("Configurators are already set".equals(e.getMessage())) {
// idempotent re-init: keep existing configurators
} else throw e;
} Prevention
- Design bootstrap so setConfigurators is called exactly once, guarded by a boolean or singleton initialization
- Make reconfiguration create a new registry rather than mutating an initialized one
- Guard against duplicate initialization in tests by using fresh registry instances per test
When it happens
Trigger: Calling setConfigurators() twice on the same registry instance, e.g. re-running initialization code, re-invoking a setup method during retry or hot-reload, or two independent components both attempting to install their configurator lists into a shared registry.
Common situations: Application bootstrap code executed more than once (redeployment hooks, Spring bean post-processing duplicates, tests reusing a registry across cases); plugin systems where multiple modules each try to register configurators; accidental reconfiguration on config refresh.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- The transport factory is closed.
- The transport factory is closed.
- Metric with name ${name} already exists
- ScheduledExecutorService not set in Builder
- ChannelLogger is not set in Builder
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/3d21bd94d1e06691.
Report an issue: GitHub.