{"record":{"id":"a4b89f95f824c2a9","repo":"dgraph-io/dgraph","slug":"cycle-detected-s","errorCode":null,"errorMessage":"Cycle detected: %s","messagePattern":"Cycle detected: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dql/parser.go","lineNumber":256,"sourceCode":"// DebugPrint is useful for debugging.\nfunc (gq *GraphQuery) DebugPrint(prefix string) {\n\tglog.Infof(\"%s[%x %q %q]\\n\", prefix, gq.UID, gq.Attr, gq.Alias)\n\tfor _, c := range gq.Children {\n\t\tc.DebugPrint(prefix + \"|->\")\n\t}\n}\n\nfunc (gq *GraphQuery) isFragment() bool {\n\treturn gq.fragment != \"\"\n}\n\nfunc (fn *fragmentNode) expand(fmap fragmentMap) error {\n\tif fn.Exited {\n\t\t// This fragment node has already been expanded.\n\t\treturn nil\n\t}\n\tif fn.Entered {\n\t\treturn errors.Errorf(\"Cycle detected: %s\", fn.Name)\n\t}\n\tfn.Entered = true\n\tif err := fn.Gq.expandFragments(fmap); err != nil {\n\t\treturn err\n\t}\n\tfn.Exited = true\n\treturn nil\n}\n\nfunc (gq *GraphQuery) expandFragments(fmap fragmentMap) error {\n\t// We have to make a copy of children to preserve order and replace\n\t// fragment references with fragment content. The copy is newChildren.\n\tvar newChildren []*GraphQuery\n\t// Expand non-fragments. Do not append to gq.Children.\n\tfor _, child := range gq.Children {\n\t\tif child.isFragment() {\n\t\t\tfname := child.fragment // Name of fragment being referenced.\n\t\t\tfchild := fmap[fname]","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/dql/parser.go#L238-L274","documentation":"fragmentNode.expand expands a GraphQL+- fragment recursively; fn.Entered marks that the fragment is currently being expanded. If expand is re-entered for the same fragment before it exits, the query contains a fragment cycle (a fragment referencing itself directly or transitively), which would loop forever, so expansion aborts with this error naming the fragment.","triggerScenarios":"Running dql parsing (via ParseWithNeedVars -> expandFragments -> expand) on a query where fragment A spreads fragment B and B spreads A (or A spreads itself), e.g. 'fragment A on X { ...B } fragment B on X { ...A }'.","commonSituations":"Copy-pasting/merging fragment definitions in large queries; refactoring queries by splitting fragments and accidentally creating mutual references; generated queries from ORMs/tools with recursive templates.","solutions":["Break the cycle by removing one of the mutual fragment spreads and inlining fields instead","Rename fragments to spot duplicates — also check that two different fragments don't share a name causing fmap collisions","Flatten the recursive structure: expand the deepest fragment's fields manually into its parent","Test the query in Ratel/Dgraph console which reports the cyclic fragment name (given in the error's %s)"],"exampleFix":"// before\nfragment A on User { ...B }\nfragment B on User { ...A }\n// after\nfragment A on User { name }\nfragment B on User { name age }","handlingStrategy":"try-catch","validationCode":"func checkNoFragmentCycle(frags map[string][]string) error {\n  var visit func(name string, seen map[string]bool) error\n  visit = func(name string, seen map[string]bool) error {\n    if seen[name] { return fmt.Errorf(\"cycle at fragment %s\", name) }\n    seen[name] = true\n    for _, dep := range frags[name] {\n      if err := visit(dep, seen); err != nil { return err }\n    }\n    delete(seen, name)\n    return nil\n  }\n  for name := range frags {\n    if err := visit(name, map[string]bool{}); err != nil { return err }\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":"err := dql.ParseWithNeedVars(query, vars)\nvar xerr *errors.Error\nif errors.As(err, &xerr) && strings.Contains(xerr.Error(), \"Cycle detected\") {\n  // fragment name is embedded after 'Cycle detected: '\n  log.Fatalf(\"mutually recursive fragment in query: %v\", xerr)\n}","preventionTips":["Keep fragment dependency graphs shallow and acyclic by design","Lint queries for recursive spreads in CI","Avoid generating fragments with templates that can self-reference"],"tags":["dgraph","dql","graphql","fragments","recursion"],"backgroundTag":"fragment-cycle-detected","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}