{"record":{"id":"497c8c05f8dbdcac","repo":"docker/compose","slug":"cycle-found-s","errorCode":null,"errorMessage":"cycle found: %s","messagePattern":"cycle found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/compose/dependencies.go","lineNumber":431,"sourceCode":"\t\tif !slices.Contains(discovered, vertex.Key) && !slices.Contains(finished, vertex.Key) {\n\t\t\tvar err error\n\t\t\tdiscovered, finished, err = g.visit(vertex.Key, path, discovered, finished)\n\t\t\tif err != nil {\n\t\t\t\treturn true, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false, nil\n}\n\nfunc (g *Graph) visit(key string, path []string, discovered []string, finished []string) ([]string, []string, error) {\n\tdiscovered = append(discovered, key)\n\n\tfor _, v := range g.Vertices[key].Children {\n\t\tpath := append(path, v.Key)\n\t\tif slices.Contains(discovered, v.Key) {\n\t\t\treturn nil, nil, fmt.Errorf(\"cycle found: %s\", strings.Join(path, \" -> \"))\n\t\t}\n\n\t\tif !slices.Contains(finished, v.Key) {\n\t\t\tif _, _, err := g.visit(v.Key, path, discovered, finished); err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\tdiscovered = slices.DeleteFunc(discovered, func(s string) bool { return s == key })\n\tfinished = append(finished, key)\n\treturn discovered, finished, nil\n}\n","sourceCodeStart":413,"sourceCodeEnd":445,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/pkg/compose/dependencies.go#L413-L445","documentation":"The dependency graph DFS (Graph.visit) found a cycle in depends_on (and other dependency edges) and reports the exact path, e.g. 'web -> api -> web'. Compose cannot determine startup order for cyclic dependencies and aborts before creating anything.","triggerScenarios":"Service A depends_on B while B depends_on A; longer loops a->b->c->a; also cycles introduced via multiple edges (depends_on plus other implicit deps) during HasCycles after graph build.","commonSituations":"Two services each waiting on the other (db healthcheck gating app while app is db's dependency); refactoring that inverts a dependency accidentally; healthcheck condition loops.","solutions":["Break the loop: make one direction optional (required: false) or drop it","Restructure so shared prerequisites are a third service both depend on","For mutual readiness, use healthchecks + restart-on-failure instead of circular depends_on","docker compose config and inspect depends_on blocks to spot the back-edge"],"exampleFix":"# before\nservices:\n  a:\n    depends_on: [b]\n  b:\n    depends_on: [a]\n# after\nservices:\n  a:\n    depends_on: [b]\n  b: {}","handlingStrategy":"validation","validationCode":"// DFS cycle check over depends_on before calling compose\nvar visit func(string, map[string]bool) error\nvisit = func(n string, seen map[string]bool) error {\n    if seen[n] { return fmt.Errorf(\"cycle at %s\", n) }\n    seen[n] = true\n    for dep := range project.Services[n].DependsOn {\n        if err := visit(dep, seen); err != nil { return err }\n    }\n    delete(seen, n)\n    return nil\n}\nfor n := range project.Services { if err := visit(n, map[string]bool{}); err != nil { return err } }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model readiness with healthchecks, not mutual depends_on","Add a compose-file linter rule for dependency cycles in CI","Keep dependency direction one-way (infra <- app)"],"tags":["dependencies","cycle-detection","compose-file"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}