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, err

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Call the paths API with a positive maxRecursion value so circular paths are expanded only to a bounded depth instead of erroring.
  2. Break the cycle in the schema: replace the recursive $ref with a concrete non-recursive type at one level.
  3. If recursion is accidental (bad $ref pointing back at its own parent), fix the $ref target.
  4. 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

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.