{"record":{"id":"e6e0b47dae6d36ba","repo":"GoogleContainerTools/skaffold","slug":"cycle-detected-involving-release-q","errorCode":null,"errorMessage":"cycle detected involving release %q","messagePattern":"cycle detected involving release %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/deploy/helm/dependencygraph.go","lineNumber":115,"sourceCode":"\t\treturn nil\n\t}\n\n\tvisited := make(map[string]bool)\n\trecStack := make(map[string]bool)\n\n\tvar checkCycle func(node string) error\n\tcheckCycle = func(node string) error {\n\t\tif !visited[node] {\n\t\t\tvisited[node] = true\n\t\t\trecStack[node] = true\n\n\t\t\tfor _, dep := range g.graph[node] {\n\t\t\t\tif !visited[dep] {\n\t\t\t\t\tif err := checkCycle(dep); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t} else if recStack[dep] {\n\t\t\t\t\treturn fmt.Errorf(\"cycle detected involving release %q\", node)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trecStack[node] = false\n\t\treturn nil\n\t}\n\n\tfor node := range g.graph {\n\t\tif !visited[node] {\n\t\t\tif err := checkCycle(node); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// getNames returns a slice of release names in their original order","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/deploy/helm/dependencygraph.go#L97-L133","documentation":"After building the release dependency graph, skaffold runs a DFS cycle check. If a release depends (directly or transitively) on itself — e.g. A depends on B and B depends on A — topological ordering is impossible and this error is returned naming one release in the cycle.","triggerScenarios":"Mutual or circular `dependsOn` entries among `deploy.helm.releases`, e.g. release A depends on B while B depends on A (or A depends on A).","commonSituations":"Copy-paste of dependsOn blocks creating a loop; refactoring charts where dependencies were reversed; accidentally self-referencing a release's own name.","solutions":["Break the cycle: remove or reorder one dependsOn link so dependencies form a DAG (e.g. make B not depend on A)","Split shared functionality into a new chart/release that both conflicting releases depend on, instead of depending on each other","Review each release's dependsOn list for self-references (A depending on A)"],"exampleFix":"// before (cycle)\nreleases:\n  - name: a\n    dependsOn: [\"b\"]\n  - name: b\n    dependsOn: [\"a\"]\n\n// after (acyclic)\nreleases:\n  - name: a\n    chartPath: charts/a\n  - name: b\n    dependsOn: [\"a\"]\n    chartPath: charts/b","handlingStrategy":"validation","validationCode":"func hasCycle(releases []Release) bool {\n    graph := map[string][]string{}\n    for _, r := range releases { graph[r.Name] = r.DependsOn }\n    var visit func(n string, seen, stack map[string]bool) bool\n    visit = func(n string, seen, stack map[string]bool) bool {\n        if stack[n] { return true }\n        if seen[n] { return false }\n        seen[n], stack[n] = true, true\n        for _, d := range graph[n] { if visit(d, seen, stack) { return true } }\n        stack[n] = false\n        return false\n    }\n    seen, stack := map[string]bool{}, map[string]bool{}\n    for n := range graph { if visit(n, seen, stack) { return true } }\n    return false\n}\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model release dependencies as a DAG and document intended ordering","Check for self-references when writing dependsOn entries","Run the cycle-check above in CI config validation before deploying"],"tags":["helm","dependency-graph","cycle"],"backgroundTag":"circular-dependency","analyzedSha":"a1189de023efc32d4b8e11f395acc678aa555011","analyzedAt":"2026-09-05T12:09:27.064Z","contentChangedAt":"2026-09-05T12:09:27.064Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}