apache/flink · error · IOException
Resource guard was already closed.
Error message
Resource guard was already closed.
What it means
ResourceGuard is a close-guard that lets a resource owner wait for outstanding leases before closing a resource (typically used by checkpoint storage). acquireResource() throws this IOException when the guard has already been closed, meaning no new clients may start using the resource because shutdown is in progress or complete.
Source
Thrown at flink-core/src/main/java/org/apache/flink/util/ResourceGuard.java:66
/** This flag indicated if it is still possible to acquire access to the resource. */
private volatile boolean closed;
public ResourceGuard() {
this.lock = new SerializableObject();
this.leaseCount = 0;
this.closed = false;
}
/**
* Acquired access from one new client for the guarded resource.
*
* @throws IOException when the resource guard is already closed.
*/
public Lease acquireResource() throws IOException {
synchronized (lock) {
if (closed) {
throw new IOException("Resource guard was already closed.");
}
++leaseCount;
}
return new Lease();
}
/**
* Releases access for one client of the guarded resource. This method must only be called after
* a matching call to {@link #acquireResource()}.
*/
private void releaseResource() {
synchronized (lock) {
--leaseCount;
if (closed && leaseCount == 0) {View on GitHub (pinned to 2f3c205e92)
Solutions
- Fix the lifecycle ordering so acquireResource() is called before close(), e.g. cancel the acquisition path (task cancellation) before triggering the guard's close.
- If racing with cancellation is expected, catch the IOException at the call site and treat it as a cancellation signal (abort the operation cleanly) rather than propagating it.
- Ensure only one component owns close() and that no code path (timer thread, async snapshot thread) tries to acquire after shutdown begins.
Example fix
// before
Lease lease = guard.acquireResource(); // may throw after close
// after
Lease lease;
try {
lease = guard.acquireResource();
} catch (IOException e) {
// resource is shutting down; abort this operation
return;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
ResourceGuard.Lease lease = guard.acquireResource();
// use resource
} catch (IOException e) {
// guard closed: treat as shutdown/cancellation, abort this operation cleanly
} Prevention
- Establish a single owner for close() and cancel acquisition paths before closing.
- Document which threads may acquire leases and ensure they observe cancellation first.
- In tests, close guards in @After only after tasks are joined.
When it happens
Trigger: Calling acquireResource() on a ResourceGuard after close() was invoked, or racing with close(): another thread closed the guard between the check and the acquisition attempt inside the synchronized block.
Common situations: Concurrent checkpoint/state-backend code that acquires access to a snapshot stream or durable data set while a task or operator is being cancelled and the guard is closed; lifecycle bugs where a component is used after its shutdown method ran.
Related errors
- Could not close resource.
- The ExecutorService is shut down already. No Callables can b
- S3ClientProvider has been closed
- Source fetch execution was interrupted
- The thread was interrupted while waiting for a fetcher task.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/92dd2e7e19823f8f.
Report an issue: GitHub.