apache/iceberg · warning

HTTP client reference count went negative key={}, refCount={

Error message

HTTP client reference count went negative key={}, refCount={}

What it means

A warning logged by HttpClientCache.release() when the reference count drops below zero, meaning more releases were issued than acquires for this client key. The cache defensively resets refCount to 0 to keep the entry usable and returns false instead of throwing.

Source

Thrown at aws/src/main/java/org/apache/iceberg/aws/HttpClientCache.java:147

    /**
     * Release a reference to the HTTP client, decrementing the reference count. If the count
     * reaches zero, the client is closed.
     *
     * @return true if the client was closed, false otherwise
     */
    synchronized boolean release() {
      if (closed) {
        LOG.warn("Attempted to release already closed HTTP client: key={}", clientKey);
        return false;
      }

      refCount--;
      LOG.debug("Released HTTP client: key={}, refCount={}", clientKey, refCount);
      if (refCount == 0) {
        return closeHttpClient();
      } else if (refCount < 0) {
        LOG.warn(
            "HTTP client reference count went negative key={}, refCount={}", clientKey, refCount);
        refCount = 0;
      }
      return false;
    }

    @VisibleForTesting
    SdkHttpClient httpClient() {
      return httpClient;
    }

    /**
     * Close the HTTP client if not already closed.
     *
     * @return true if the client was closed by this call, false if already closed
     */
    private boolean closeHttpClient() {
      if (!closed) {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Match every release() 1:1 with an acquire(); never release a client you didn't acquire for the same key
  2. Check whether an exception path releases both where it failed and in finally, and remove the duplicate
  3. If you see this repeatedly, log the calling stack at your call site to find the unbalanced release
  4. Rely on the cache's self-heal (refCount reset to 0) only as a safety net, not as expected behavior

Example fix

// before
if (condition) {
  cache.releaseClient(key); // released without ever acquiring
}
// after
S3Client client = cache.acquire(key);
try {
  use(client);
} finally {
  cache.releaseClient(key);
}
Defensive patterns

Strategy: validation

Validate before calling

// ensure acquire happened before release
if (!acquiredKeys.contains(key)) {
  throw new IllegalStateException("release without acquire for " + key);
}

Prevention

When it happens

Trigger: An unbalanced release with no matching acquire, or a double release racing past the refCount==0 close check. Because release() is synchronized, the negative value is only observable when the closed-flag guard didn't fire first (e.g. entry recreated between calls).

Common situations: Utility wrappers that release a client they never acquired; refactoring removed an acquire but left the release; concurrent callers sharing a key with hand-rolled counting that drifts.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/ea0f984f5d871f72. Report an issue: GitHub.