bumptech/glide · error · IllegalStateException

You cannot auto lock an already locked options object, try c

Error message

You cannot auto lock an already locked options object, try clone() first

What it means

Thrown by BaseRequestOptions.autoClone() when the options object is already locked (isLocked == true) but auto-clone has not yet been enabled. autoClone() turns a locked object into an auto-cloning one so that subsequent mutations transparently return clones, but you cannot retroactively flip this state on a plain-locked object because the caller may already hold a reference expecting immutability.

Source

Thrown at library/src/main/java/com/bumptech/glide/request/BaseRequestOptions.java:1303

  @SuppressWarnings("unchecked")
  public T lock() {
    isLocked = true;
    // This is the only place we should not check locked.
    return self();
  }

  /**
   * Similar to {@link #lock()} except that mutations cause a {@link #clone()} operation to happen
   * before the mutation resulting in all methods returning a new Object and leaving the original
   * locked object unmodified.
   *
   * <p>Auto clone is not retained by cloned objects returned from mutations. The cloned objects are
   * mutable and are not locked.
   */
  @NonNull
  public T autoClone() {
    if (isLocked && !isAutoCloneEnabled) {
      throw new IllegalStateException(
          "You cannot auto lock an already locked options object" + ", try clone() first");
    }
    isAutoCloneEnabled = true;
    return lock();
  }

  @NonNull
  @SuppressWarnings("unchecked")
  protected final T selfOrThrowIfLocked() {
    if (isLocked) {
      throw new IllegalStateException("You cannot modify locked T, consider clone()");
    }
    return self();
  }

  protected final boolean isAutoCloneEnabled() {
    return isAutoCloneEnabled;
  }

View on GitHub (pinned to eb14a895d8)

Solutions

  1. Call autoClone() on a fresh, unlocked clone: requestOptions.clone().autoClone()
  2. Enable autoClone before the object ever gets locked (i.e. before it is applied to any request)
  3. Avoid sharing a single mutable RequestOptions across many requests; build a new one per use or freeze a template and clone from it
  4. Use RequestOptions.diskCacheStrategyOf(...) / factory-style helpers which return independent immutable instances

Example fix

// before
val shared = RequestOptions().centerCrop()
Glide.with(ctx).load(url).apply(shared).into(iv) // apply may lock shared
shared.autoClone() // IllegalStateException

// after
val template = RequestOptions().centerCrop()
val perRequest = template.clone().autoClone()
Glide.with(ctx).load(url).apply(perRequest).into(iv)
Defensive patterns

Strategy: validation

Validate before calling

fun RequestOptions.autoCloneSafe(): RequestOptions =
  if (isLocked) clone().autoClone() else autoClone()

Prevention

When it happens

Trigger: Calling RequestOptions.lock() followed by RequestOptions.autoClone() on the same instance. lock() is applied internally by Glide when options are applied to a request (e.g. into()), so reusing the same RequestOptions builder across multiple loads and then calling autoClone() triggers it.

Common situations: Library/SDK authors building a shared RequestOptions singleton, calling apply() (which locks) and later autoClone(); calling autoClone() on options returned from Glide.init or from an already-built request.

Related errors


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