{"record":{"id":"3994e1daa144bc64","repo":"google/gson","slug":"adapter-for-type-with-cyclic-dependency-has-been-u","errorCode":null,"errorMessage":"Adapter for type with cyclic dependency has been used before dependency has been resolved","messagePattern":"Adapter for type with cyclic dependency has been used before dependency has been resolved","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/Gson.java","lineNumber":1255,"sourceCode":"   * @see Gson#threadLocalAdapterResults\n   */\n  static class FutureTypeAdapter<T> extends SerializationDelegatingTypeAdapter<T> {\n    private TypeAdapter<T> delegate = null;\n\n    public void setDelegate(TypeAdapter<T> typeAdapter) {\n      if (delegate != null) {\n        throw new AssertionError(\"Delegate is already set\");\n      }\n      delegate = typeAdapter;\n    }\n\n    private TypeAdapter<T> delegate() {\n      TypeAdapter<T> delegate = this.delegate;\n      if (delegate == null) {\n        // Can occur when adapter is leaked to other thread or when adapter is used for\n        // (de-)serialization\n        // directly within the TypeAdapterFactory which requested it\n        throw new IllegalStateException(\n            \"Adapter for type with cyclic dependency has been used\"\n                + \" before dependency has been resolved\");\n      }\n      return delegate;\n    }\n\n    @Override\n    public TypeAdapter<T> getSerializationDelegate() {\n      return delegate();\n    }\n\n    @Override\n    public T read(JsonReader in) throws IOException {\n      return delegate().read(in);\n    }\n\n    @Override\n    public void write(JsonWriter out, T value) throws IOException {","sourceCodeStart":1237,"sourceCodeEnd":1273,"githubUrl":"https://github.com/google/gson/blob/310ac341f2f92a454b229bf21f70d2d18b2b6db7/gson/src/main/java/com/google/gson/Gson.java#L1237-L1273","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: using adapter inside create() during cycle resolution\nclass MyFactory implements TypeAdapterFactory {\n  public <T> TypeAdapter<T> create(Gson g, TypeToken<T> t) {\n    TypeAdapter<Partner> p = g.getAdapter(Partner.class);\n    p.fromJsonTree(...); // delegate not set -> throws\n  }\n}\n\n// after: capture adapter, use lazily in read()/write()\nTypeAdapter<Partner> p = g.getAdapter(Partner.class);\nreturn new TypeAdapter<T>() {\n  public T read(JsonReader in) { /* use p here, after resolution */ }\n};","handlingStrategy":"validation","validationCode":"// Do not use adapters obtained during create(); capture and use lazily.\nclass MyFactory implements TypeAdapterFactory {\n  public <T> TypeAdapter<T> create(Gson g, TypeToken<T> t) {\n    final TypeAdapter<Partner> partner = g.getAdapter(Partner.class); // OK to obtain\n    return new TypeAdapter<T>() {\n      public T read(JsonReader in) {\n        // use partner here, AFTER construction\n        Partner p = partner.read(in);\n        return /* build T */;\n      }\n      public void write(JsonWriter out, T v) { /* use partner here */ }\n    };\n  }\n}","typeGuard":null,"tryCatchPattern":"try { adapter.read(reader); }\ncatch (IllegalStateException e) {\n  if (e.getMessage().contains(\"cyclic dependency\")) {\n    // restructure factory to defer adapter usage out of create()\n  } else throw e;\n}","preventionTips":["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."],"tags":["gson-core","cyclic-dependency","type-adapter","thread-safety"],"backgroundTag":null,"analyzedSha":"310ac341f2f92a454b229bf21f70d2d18b2b6db7","analyzedAt":"2026-08-10T02:58:47.455Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}