apache/iceberg · warning
Attempted to release already closed HTTP client: key={}
Error message
Attempted to release already closed HTTP client: key={} What it means
This is a warning log (not an exception) from the internal reference-counted HTTP client cache in HttpClientCache. Each acquire() increments a ref count per client key and each release() decrements it; when the count reaches zero the client is closed. Seeing this means release() was called on a client whose entry was already closed, so the release is ignored and returns false.
Source
Thrown at aws/src/main/java/org/apache/iceberg/aws/HttpClientCache.java:138
*/
synchronized ManagedHttpClient acquire() {
if (closed) {
throw new IllegalStateException("Cannot acquire closed HTTP client: " + clientKey);
}
refCount++;
LOG.debug("Acquired HTTP client: key={}, refCount={}", clientKey, refCount);
return this;
}
/**
* 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;View on GitHub (pinned to 86d9c8fc54)
Solutions
- Audit call sites so release() is called exactly once per acquire(), typically in a single finally block
- Guard with your own boolean flag so a double release can't happen on retry/failure paths
- Treat the warning as benign if it comes from tests intentionally probing double-release; the call returns false and takes no action
- Ensure no code path closes the cache entry externally before all users finish with the client
Example fix
// before
S3Client client = cache.acquire(key);
try {
use(client);
} finally {
cache.releaseClient(key);
cache.releaseClient(key); // second release logs the warning
}
// after
S3Client client = cache.acquire(key);
try {
use(client);
} finally {
cache.releaseClient(key); // exactly one release per acquire
} Defensive patterns
Strategy: try-catch
Validate before calling
// track balance yourself int acquired = 0; acquired++; client = cache.acquire(key); // assert before releasing assert acquired > 0;
Type guard
boolean canRelease = acquiredCount.get(key) != null && acquiredCount.get(key) > 0;
Try / catch
// release() never throws; just don't double-release
try {
use(client);
} finally {
if (released.compareAndSet(false, true)) {
cache.releaseClient(key);
}
} Prevention
- Release exactly once per acquire in one finally block
- Use an AtomicBoolean/compareAndSet guard for idempotent release
- Don't release clients you didn't acquire for the same key
- Treat the warning log as a signal of an unbalanced lifecycle bug
When it happens
Trigger: Calling HttpClientCache.releaseClient (or the internal release()) more times than acquire() for the same client key, or after the entry was closed by another thread due to the ref count hitting zero. The tests closed, closedAgain, multipleReleasesAfterClose exercise exactly this double-release path.
Common situations: A caller wraps client usage in a finally block that releases even when acquire failed or an earlier release already ran; a race where two threads both observe refCount==1 and both call release; a retry wrapper that releases the client on both the failure and success paths.
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
- HTTP client reference count went negative key={}, refCount={
- Cannot acquire closed HTTP client: ${clientKey}
- Unrecognized HTTP client type ${httpClientType}
- Unrecognized HTTP client type ${httpClientType}
- Cannot create %s to generate and configure the http client b
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/47e075c4ccd831e6.
Report an issue: GitHub.