bumptech/glide · error · IllegalStateException
Already released
Error message
Already released
What it means
Thrown by StateVerifier.DefaultStateVerifier.throwIfRecycled when isReleased is true. StateVerifier guards pooled/recycled objects (notably SingleRequest and other request objects in Glide's pool) so they are not reused after release/recycle. Reuse of a recycled object would produce corrupted state and stale callbacks, so the guard fails fast.
Source
Thrown at library/src/main/java/com/bumptech/glide/util/pool/StateVerifier.java:40
/**
* Throws an exception if we believe our object is recycled and inactive (i.e. is currently in an
* object pool).
*/
public abstract void throwIfRecycled();
/** Sets whether or not our object is recycled. */
abstract void setRecycled(boolean isRecycled);
private static class DefaultStateVerifier extends StateVerifier {
private volatile boolean isReleased;
@Synthetic
DefaultStateVerifier() {}
@Override
public void throwIfRecycled() {
if (isReleased) {
throw new IllegalStateException("Already released");
}
}
@Override
public void setRecycled(boolean isRecycled) {
this.isReleased = isRecycled;
}
}
private static class DebugStateVerifier extends StateVerifier {
// Keeps track of the stack trace where our state was set to recycled.
private volatile RuntimeException recycledAtStackTraceException;
@Synthetic
DebugStateVerifier() {}
@Override
public void throwIfRecycled() {View on GitHub (pinned to eb14a895d8)
Solutions
- Do not retain references to Glide Request objects; let RequestManager/into(ImageView) own the lifecycle
- For custom Targets, null out the reference after onLoadCleared and stop using it
- In RecyclerView, clear targets in onViewRecycled and create fresh loads on rebind
- If you suspect a Glide bug, enable DebugStateVerifier (StateVerifier.DEBUG=true via reflection in a debug build) to capture the stack trace of the original release
Example fix
// before
class MyHolder(itemView: View) {
var target: Target<Drawable>? = null
fun bind(url: String) {
target = Glide.with(itemView).load(url).into(imageView)
}
fun onRecycled() {
target // still held; later use throws 'Already released'
}
}
// after
class MyHolder(itemView: View) {
fun bind(url: String) {
Glide.with(itemView).load(url).into(imageView) // Glide tracks the request
}
fun onRecycled() {
Glide.with(itemView).clear(imageView)
}
} Defensive patterns
Strategy: validation
Validate before calling
// Don't hold Request references; let Glide own them. For custom Targets:
@Volatile var cleared = false
override fun onLoadCleared(placeholder: Drawable?) {
cleared = true
// null out references, stop using this target
} Prevention
- Do not retain Glide Request/Target references after clear/onLoadCleared
- Use the high-level into(ImageView) API so Glide owns request pooling
- Clear targets in RecyclerView.onViewRecycled; create fresh loads on rebind
- In debug builds, enable StateVerifier.DEBUG via reflection to capture the original release stack
When it happens
Trigger: Holding a reference to a Glide Request/Target after it has been cleared/recycled and then calling a method on it (begin, clear, pause, etc.); Double-clearing a request; using a ViewTarget after Glide cleared it and the underlying request was returned to the pool.
Common situations: Caching a Request or Target in a field and reusing it across config changes; RecyclerView ViewHolders that retain stale Targets after onViewRecycled; calling request.begin() after RequestManager.clear recycled it; library bugs that re-emit callbacks on a recycled request.
Related errors
- You cannot start a load on a fragment before it is attached
- You cannot auto lock an already locked options object, try c
- You cannot modify locked T, consider clone()
- Cannot restart a running request
- You can't start or clear loads in RequestListener or Target
AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14).
Data as JSON: /api/errors/61964a3d6038d584.
Report an issue: GitHub.