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

  1. Do not retain references to Glide Request objects; let RequestManager/into(ImageView) own the lifecycle
  2. For custom Targets, null out the reference after onLoadCleared and stop using it
  3. In RecyclerView, clear targets in onViewRecycled and create fresh loads on rebind
  4. 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

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


AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14). Data as JSON: /api/errors/61964a3d6038d584. Report an issue: GitHub.