google/gson · error · IllegalStateException
Adapter for type with cyclic dependency has been used…
Error message
Adapter for type with cyclic dependency has been used before dependency has been resolved
What it means
Thrown by Gson.FutureTypeAdapter.delegate() when a type adapter is used (read/write called) before its delegate has been set. FutureTypeAdapter is a placeholder created during resolution of cyclic type graphs (type A -> type B -> type A); the real adapter is wired later via setDelegate. Using it too early means the dependency cycle has not been resolved, so an IllegalStateException is thrown. It can also occur when a placeholder leaks to another thread before publication.
Solutions
- Do not invoke read()/write() on adapters obtained during create(); store them as fields and use them lazily at (de)serialization time, not at factory-construction time.
- Defer adapter lookup to the actual read/write call rather than resolving it inside create().
- Avoid sharing a Gson instance being constructed across threads; complete create() before publishing.
- If a cycle must be broken, introduce an InstanceCreator or a hand-written adapter that does not require the partner adapter during construction.
Example fix
// before: using adapter inside create() during cycle resolution
class MyFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson g, TypeToken<T> t) {
TypeAdapter<Partner> p = g.getAdapter(Partner.class);
p.fromJsonTree(...); // delegate not set -> throws
}
}
// after: capture adapter, use lazily in read()/write()
TypeAdapter<Partner> p = g.getAdapter(Partner.class);
return new TypeAdapter<T>() {
public T read(JsonReader in) { /* use p here, after resolution */ }
}; Defensive patterns
Strategy: validation
Validate before calling
// Do not use adapters obtained during create(); capture and use lazily.
class MyFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson g, TypeToken<T> t) {
final TypeAdapter<Partner> partner = g.getAdapter(Partner.class); // OK to obtain
return new TypeAdapter<T>() {
public T read(JsonReader in) {
// use partner here, AFTER construction
Partner p = partner.read(in);
return /* build T */;
}
public void write(JsonWriter out, T v) { /* use partner here */ }
};
}
} Try / catch
try { adapter.read(reader); }
catch (IllegalStateException e) {
if (e.getMessage().contains("cyclic dependency")) {
// restructure factory to defer adapter usage out of create()
} else throw e;
} Prevention
- Never call read()/write() on adapters obtained inside create(); defer to actual (de)serialization.
- Complete Gson construction before sharing it across threads.
- Break genuine cycles with an InstanceCreator or hand-written adapter.
When it happens
Trigger: Within a TypeAdapterFactory.create(), obtaining an adapter for a cyclically-dependent type and immediately using it to (de)serialize; calling gson.getAdapter() inside create() for the partner of a cycle and then invoking read/write on it; multithreaded construction where a FutureTypeAdapter is observed before setDelegate completes.
Common situations: Entity graphs with bidirectional references (Parent<->Child) where a factory eagerly serializes; custom factories that fetch and use adapters for self-referential types during create(); race conditions sharing a partially-built Gson across threads.
Related errors
- GSON cannot serialize or deserialize
- GSON ( ) cannot handle
- Type adapter ' ' returned wrong type; requested but got…
- Attempted to deserialize a java.lang.Class. Forgot to…
- Attempted to serialize java.lang.Class
AI-assisted analysis of google/gson@310ac341f2 (2026-08-10).
Data as JSON: /api/errors/3994e1daa144bc64.
Report an issue: GitHub.
Appendix: source
Thrown at gson/src/main/java/com/google/gson/Gson.java:1255
* @see Gson#threadLocalAdapterResults
*/
static class FutureTypeAdapter<T> extends SerializationDelegatingTypeAdapter<T> {
private TypeAdapter<T> delegate = null;
public void setDelegate(TypeAdapter<T> typeAdapter) {
if (delegate != null) {
throw new AssertionError("Delegate is already set");
}
delegate = typeAdapter;
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> delegate = this.delegate;
if (delegate == null) {
// Can occur when adapter is leaked to other thread or when adapter is used for
// (de-)serialization
// directly within the TypeAdapterFactory which requested it
throw new IllegalStateException(
"Adapter for type with cyclic dependency has been used"
+ " before dependency has been resolved");
}
return delegate;
}
@Override
public TypeAdapter<T> getSerializationDelegate() {
return delegate();
}
@Override
public T read(JsonReader in) throws IOException {
return delegate().read(in);
}
@Override
public void write(JsonWriter out, T value) throws IOException {View on GitHub (pinned to 310ac341f2)