grpc/grpc-java · warning · IllegalStateException
GcpObservability already closed!
Error message
GcpObservability already closed!
What it means
GcpObservability.closeWithSleepTime closes the observability sink but requires a live singleton instance; if GcpObservability was already closed (instance set to null) it throws IllegalStateException. This guards against double-close of the global observability registrar.
Solutions
- Track whether close was already called and only call it once (idempotent close wrapper).
- Null-check via your own holder before calling close, or catch IllegalStateException and ignore.
- Ensure only one component owns the GcpObservability lifecycle.
Example fix
// before
observability.close();
cleanupHook.run(); // calls observability.close() again -> IllegalStateException
// after
private final AtomicBoolean closed = new AtomicBoolean(false);
if (closed.compareAndSet(false, true)) { observability.close(); } Defensive patterns
Strategy: try-catch
Validate before calling
private final AtomicBoolean closed = new AtomicBoolean(false); boolean shouldClose = closed.compareAndSet(false, true);
Try / catch
try { observability.close(); } catch (IllegalStateException e) { /* already closed; ignore */ } Prevention
- Make close idempotent with a flag
- Ensure only one lifecycle owner calls close
- Avoid calling close from both shutdown hook and app teardown
When it happens
Trigger: Calling GcpObservability.close() (or closeWithSleepTime) a second time after the singleton was already closed.
Common situations: Shutdown hooks plus application teardown both calling close(); wrapping observability close in try/finally that runs twice; repeated Spring bean destruction.
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
- Handshaker service error
- A key manager is required
- A terminal HttpFilter must be the last filter
- Address is not an IP
- Address types of NameResolver
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/31aad64ce7e5da10.
Report an issue: GitHub.
Appendix: source
Thrown at gcp-observability/src/main/java/io/grpc/gcp/observability/GcpObservability.java:141
}
return instance;
}
/** Un-initialize/shutdown grpc-observability. */
@Override
public void close() {
closeWithSleepTime(2 * METRICS_EXPORT_INTERVAL, TimeUnit.SECONDS);
}
/**
* Method to close along with sleep time explicitly.
*
* @param sleepTime sleepTime
*/
void closeWithSleepTime(long sleepTime, TimeUnit timeUnit) {
synchronized (GcpObservability.class) {
if (instance == null) {
throw new IllegalStateException("GcpObservability already closed!");
}
sink.close();
if (config.isEnableCloudMonitoring() || config.isEnableCloudTracing()) {
try {
// Sleeping before shutdown to ensure all metrics and traces are flushed
timeUnit.sleep(sleepTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.log(Level.SEVERE, "Caught exception during sleep", e);
}
}
instance = null;
}
}
// TODO(dnvindhya): Remove <channel/server>InterceptorFactory and replace with respective
// interceptors
private void setProducer(View on GitHub (pinned to 64daddc1f3)