ory/hydra · error
detected circular dependency in schema path: %s
Error message
detected circular dependency in schema path: %s
What it means
listPaths detects cycles while recursively walking JSON Schema properties (a $ref chain that leads back to itself). When a cycle is detected and maxRecursion is -1 (cycle resolution disabled), the walk cannot terminate safely, so it errors with the dotted path chain that loops. Callers can instead set maxRecursion to a positive value to allow bounded expansion of the cycle.
Source
Thrown at oryx/jsonschemax/keys.go:348
MultipleOf: schema.MultipleOf,
ReadOnly: schema.ReadOnly,
Title: schema.Title,
Description: schema.Description,
Examples: schema.Examples,
Required: required,
}
for _, e := range schema.Extensions {
if enhancer, ok := e.(PathEnhancer); ok {
path.CustomProperties = enhancer.EnhancePath(path)
}
}
paths = append(paths, path)
}
if isCircular {
if maxRecursion == -1 {
return nil, errors.Errorf("detected circular dependency in schema path: %s", strings.Join(parents, "."))
} else if currentRecursion > maxRecursion {
return paths, nil
}
currentRecursion++
}
if schema.Ref != nil {
path, err := listPaths(schema.Ref, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)
if err != nil {
return nil, err
}
paths = append(paths, path...)
}
if schema.Not != nil {
path, err := listPaths(schema.Not, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)
if err != nil {
return nil, errView on GitHub (pinned to 4174065ffb)
Solutions
- Call the paths API with a positive maxRecursion value so circular paths are expanded only to a bounded depth instead of erroring.
- Break the cycle in the schema: replace the recursive $ref with a concrete non-recursive type at one level.
- If recursion is accidental (bad $ref pointing back at its own parent), fix the $ref target.
- Post-process: extract the paths up to the first repeated segment from the error message and generate keys manually.
Example fix
// before
paths, err := jsonschemax.ListPathsWithCache("schema.json") // circular $ref -> error
// after
cache, _ := jsonschemax.NewCache("schema.json", jsonschemax.WithMaxRecursion(3))
paths := cache.ListPaths() Defensive patterns
Strategy: validation
Validate before calling
// Detect circular $refs before walking the schema
func hasCycle(def string, defs map[string]map[string]interface{}, seen map[string]bool) bool {
if seen[def] { return true }
seen[def] = true
for _, ref := range refsIn(defs[def]) {
if hasCycle(ref, defs, seen) { return true }
}
return false
} Prevention
- Prefer passing a positive maxRecursion (jsonschemax.WithMaxRecursion) for schemas that may be recursive.
- Lint schemas for self-referencing $ref chains before codegen.
- Design recursive message types with an explicit depth cutoff.
When it happens
Trigger: Running runPaths/ListPathsWithCache on a schema containing a circular $ref (e.g., {"$ref": "#/definitions/node"} whose definition references itself) with maxRecursion == -1, at oryx/jsonschemax/keys.go:348.
Common situations: Recursive schema structures common in real APIs: linked lists, tree nodes, self-referential message types, or mutually recursive definitions generated from protobuf/OpenAPI with recursive messages.
Related errors
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/ab4c4d4398195878.
Report an issue: GitHub.