{"record":{"id":"4dca1226e12c8d33","repo":"JamesNK/Newtonsoft.Json","slug":"unresolved-circular-reference-for-type-0-expl","errorCode":null,"errorMessage":"Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.","messagePattern":"Unresolved circular reference for type '(.+?)'\\. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs","lineNumber":282,"sourceCode":"                    // resolved schema is not null but referencing member allows nulls\n                    // change resolved schema to allow nulls. hacky but what are ya gonna do?\n                    if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null))\n                    {\n                        resolvedSchema.Type |= JsonSchemaType.Null;\n                    }\n                    if (required && resolvedSchema.Required != true)\n                    {\n                        resolvedSchema.Required = true;\n                    }\n\n                    return resolvedSchema;\n                }\n            }\n\n            // test for unresolved circular reference\n            if (_stack.Any(tc => tc.Type == type))\n            {\n                throw new JsonException(\"Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.\".FormatWith(CultureInfo.InvariantCulture, type));\n            }\n\n            JsonContract contract = ContractResolver.ResolveContract(type);\n            JsonConverter converter = contract.Converter ?? contract.InternalConverter;\n\n            Push(new TypeSchema(type, new JsonSchema()));\n\n            if (explicitId != null)\n            {\n                CurrentSchema.Id = explicitId;\n            }\n\n            if (required)\n            {\n                CurrentSchema.Required = true;\n            }\n            CurrentSchema.Title = GetTitle(type);\n            CurrentSchema.Description = GetDescription(type);","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/JamesNK/Newtonsoft.Json/blob/4f73e74372445108d2c1bda37b36e6f5e43402e0/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs#L264-L300","documentation":"Thrown by JsonSchemaGenerator.GenerateInternal when it detects that the type being processed is already on the generation stack (a cycle), and neither a JsonObject/JsonArray Id attribute nor UndefinedSchemaIdHandling (UseTypeName/UseAssemblyQualifiedName) is configured to break the cycle. Without an id, the generator cannot emit a $ref and would recurse infinitely, so it aborts.","triggerScenarios":"Calling JsonSchemaGenerator.Generate on a type with a self-referencing or mutually-referencing property and no schema id configured. Example: class Node { public Node Next { get; set; } } with the default UndefinedSchemaIdHandling (None) throws; class A { public B B { get; set; } } / class B { public A A { get; set; } } likewise.","commonSituations":"Domain models with parent/child or graph relationships (tree nodes, linked lists, graphs) serialized to JSON Schema; upgrading from a version where ids were inferred; changing UndefinedSchemaIdHandling back to None.","solutions":["Decorate the cyclic type with [JsonObject(Id = \"node\")] or [JsonArray(Id = \"node\")] so the generator emits a $ref.","Set generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName to auto-generate ids from type names.","Break the cycle by marking the back-reference property with [JsonIgnore]."],"exampleFix":"// before\nclass Node { public Node Next { get; set; } }\ngen.Generate(typeof(Node)); // throws\n// after\n[JsonObject(Id = \"node\")]\nclass Node { public Node Next { get; set; } }\ngen.Generate(typeof(Node));","handlingStrategy":"validation","validationCode":"static bool HasSchemaId(Type type)\n{\n    var attr = type.GetCustomAttributes(typeof(JsonObjectAttribute), false)\n        .Cast<JsonObjectAttribute>().FirstOrDefault();\n    return attr != null && !string.IsNullOrEmpty(attr.Id);\n}","typeGuard":null,"tryCatchPattern":"try { generator.Generate(typeof(T)); }\ncatch (JsonException ex) when (ex.Message.StartsWith(\"Unresolved circular reference\"))\n{ /* add [JsonObject(Id=...)] or set UndefinedSchemaIdHandling */ }","preventionTips":["Decorate recursive types with [JsonObject(Id = \"...\")] or [JsonArray(Id = \"...\")].","Set UndefinedSchemaIdHandling.UseTypeName for automatic ids.","Mark back-references with [JsonIgnore] to break cycles."],"tags":["schema","json","jsonschema","circular-reference","codegen"],"analyzedSha":"4f73e74372445108d2c1bda37b36e6f5e43402e0","analyzedAt":"2026-08-07T06:10:08.596Z","schemaVersion":2},"datasetVersion":"2026-08-07T07:17:06.508Z"}