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
- Call autoClone() on a fresh, unlocked clone: requestOptions.clone().autoClone()
- Enable autoClone before the object ever gets locked (i.e. before it is applied to any request)
- Avoid sharing a single mutable RequestOptions across many requests; build a new one per use or freeze a template and clone from it
- 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
- Call autoClone() on a fresh unlocked instance, never on one already applied to a request
- Treat RequestOptions stored in fields as immutable templates: always clone() before mutating
- Avoid lock() then autoClone() on the same instance
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
- You cannot modify locked T, consider clone()
- sizeMultiplier must be between 0 and 1
- Already released
- You cannot start a load on a fragment before it is attached
- Unable to find GlideModule implementation
AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14).
Data as JSON: /api/errors/7047531a536a5dcb.
Report an issue: GitHub.