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

  1. Track whether close was already called and only call it once (idempotent close wrapper).
  2. Null-check via your own holder before calling close, or catch IllegalStateException and ignore.
  3. 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

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


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)