{"record":{"id":"ab4c4d4398195878","repo":"ory/hydra","slug":"detected-circular-dependency-in-schema-path-s","errorCode":null,"errorMessage":"detected circular dependency in schema path: %s","messagePattern":"detected circular dependency in schema path: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/jsonschemax/keys.go","lineNumber":348,"sourceCode":"\t\t\tMultipleOf:  schema.MultipleOf,\n\t\t\tReadOnly:    schema.ReadOnly,\n\t\t\tTitle:       schema.Title,\n\t\t\tDescription: schema.Description,\n\t\t\tExamples:    schema.Examples,\n\t\t\tRequired:    required,\n\t\t}\n\n\t\tfor _, e := range schema.Extensions {\n\t\t\tif enhancer, ok := e.(PathEnhancer); ok {\n\t\t\t\tpath.CustomProperties = enhancer.EnhancePath(path)\n\t\t\t}\n\t\t}\n\t\tpaths = append(paths, path)\n\t}\n\n\tif isCircular {\n\t\tif maxRecursion == -1 {\n\t\t\treturn nil, errors.Errorf(\"detected circular dependency in schema path: %s\", strings.Join(parents, \".\"))\n\t\t} else if currentRecursion > maxRecursion {\n\t\t\treturn paths, nil\n\t\t}\n\t\tcurrentRecursion++\n\t}\n\n\tif schema.Ref != nil {\n\t\tpath, err := listPaths(schema.Ref, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpaths = append(paths, path...)\n\t}\n\n\tif schema.Not != nil {\n\t\tpath, err := listPaths(schema.Not, schema, parents, appendPointer(pointers, schema), currentRecursion, maxRecursion, includeArrays)\n\t\tif err != nil {\n\t\t\treturn nil, err","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/jsonschemax/keys.go#L330-L366","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npaths, err := jsonschemax.ListPathsWithCache(\"schema.json\") // circular $ref -> error\n// after\ncache, _ := jsonschemax.NewCache(\"schema.json\", jsonschemax.WithMaxRecursion(3))\npaths := cache.ListPaths()","handlingStrategy":"validation","validationCode":"// Detect circular $refs before walking the schema\nfunc hasCycle(def string, defs map[string]map[string]interface{}, seen map[string]bool) bool {\n    if seen[def] { return true }\n    seen[def] = true\n    for _, ref := range refsIn(defs[def]) {\n        if hasCycle(ref, defs, seen) { return true }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["json-schema","recursion","circular-reference"],"backgroundTag":"circular-schema-reference","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}