JamesNK/Newtonsoft.Json · error · JsonException

Unresolved circular reference for type '{0}'. Explicitly def

Error message

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.

What it means

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.

Source

Thrown at Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs:282

                    // resolved schema is not null but referencing member allows nulls
                    // change resolved schema to allow nulls. hacky but what are ya gonna do?
                    if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null))
                    {
                        resolvedSchema.Type |= JsonSchemaType.Null;
                    }
                    if (required && resolvedSchema.Required != true)
                    {
                        resolvedSchema.Required = true;
                    }

                    return resolvedSchema;
                }
            }

            // test for unresolved circular reference
            if (_stack.Any(tc => tc.Type == type))
            {
                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));
            }

            JsonContract contract = ContractResolver.ResolveContract(type);
            JsonConverter converter = contract.Converter ?? contract.InternalConverter;

            Push(new TypeSchema(type, new JsonSchema()));

            if (explicitId != null)
            {
                CurrentSchema.Id = explicitId;
            }

            if (required)
            {
                CurrentSchema.Required = true;
            }
            CurrentSchema.Title = GetTitle(type);
            CurrentSchema.Description = GetDescription(type);

View on GitHub (pinned to 4f73e74372)

Solutions

  1. Decorate the cyclic type with [JsonObject(Id = "node")] or [JsonArray(Id = "node")] so the generator emits a $ref.
  2. Set generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName to auto-generate ids from type names.
  3. Break the cycle by marking the back-reference property with [JsonIgnore].

Example fix

// before
class Node { public Node Next { get; set; } }
gen.Generate(typeof(Node)); // throws
// after
[JsonObject(Id = "node")]
class Node { public Node Next { get; set; } }
gen.Generate(typeof(Node));
Defensive patterns

Strategy: validation

Validate before calling

static bool HasSchemaId(Type type)
{
    var attr = type.GetCustomAttributes(typeof(JsonObjectAttribute), false)
        .Cast<JsonObjectAttribute>().FirstOrDefault();
    return attr != null && !string.IsNullOrEmpty(attr.Id);
}

Try / catch

try { generator.Generate(typeof(T)); }
catch (JsonException ex) when (ex.Message.StartsWith("Unresolved circular reference"))
{ /* add [JsonObject(Id=...)] or set UndefinedSchemaIdHandling */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of JamesNK/Newtonsoft.Json@4f73e74372 (2026-08-07). Data as JSON: /api/errors/4dca1226e12c8d33. Report an issue: GitHub.