{"record":{"id":"95ce696a6c26c25a","repo":"GoogleContainerTools/skaffold","slug":"cycle-detected-in-build-dependencies-involving-q","errorCode":null,"errorMessage":"cycle detected in build dependencies involving %q","messagePattern":"cycle detected in build dependencies involving %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/schema/validation/validation.go","lineNumber":239,"sourceCode":"\t\tm[artifact.ImageName] = artifact\n\t}\n\tvisited := make(map[string]bool)\n\tfor i, artifact := range artifacts {\n\t\tif err := dfs(artifact, visited, make(map[string]bool), m); err != nil {\n\t\t\tcfgErrs = append(cfgErrs, ErrorWithLocation{\n\t\t\t\tError:    err,\n\t\t\t\tLocation: cfgs.Locate(artifacts[i]),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\n// dfs runs a Depth First Search algorithm for cycle detection in a directed graph\nfunc dfs(artifact *latest.Artifact, visited, marked map[string]bool, artifacts map[string]*latest.Artifact) error {\n\tif marked[artifact.ImageName] {\n\t\treturn fmt.Errorf(\"cycle detected in build dependencies involving %q\", artifact.ImageName)\n\t}\n\tmarked[artifact.ImageName] = true\n\tdefer func() {\n\t\tmarked[artifact.ImageName] = false\n\t}()\n\tif visited[artifact.ImageName] {\n\t\treturn nil\n\t}\n\tvisited[artifact.ImageName] = true\n\n\tfor _, dep := range artifact.Dependencies {\n\t\td, found := artifacts[dep.ImageName]\n\t\tif !found {\n\t\t\treturn fmt.Errorf(\"unknown build dependency %q for artifact %q\", dep.ImageName, artifact.ImageName)\n\t\t}\n\t\tif err := dfs(d, visited, marked, artifacts); err != nil {\n\t\t\treturn err\n\t\t}","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/schema/validation/validation.go#L221-L257","documentation":"dfs runs a depth-first search over the artifact build-dependency graph to detect cycles. If an artifact is re-entered while still on the current DFS path (marked), the dependencies form a cycle and validation fails. Cycles are impossible to build, since each artifact would wait on another to finish.","triggerScenarios":"Artifact A declares buildDependencies on B, and B (directly or transitively) declares a dependency back on A, so dfs revisits an artifact still marked on the active path.","commonSituations":"Circular requires/depends_on entries after refactoring multi-artifact skaffold.yaml files, or accidentally listing the artifact itself as its own dependency.","solutions":["Trace the cycle named in the error and remove one edge so dependencies form a DAG (e.g. drop A's dependency on B)","Build shared code into a separate base image that both artifacts depend on, instead of cross-depending","Check for an artifact listing itself in its own buildDependencies"],"exampleFix":"// before\nartifacts:\n  - image: app-a\n    requires: [{ image: app-b }]\n  - image: app-b\n    requires: [{ image: app-a }]\n// after\nartifacts:\n  - image: base-lib\n  - image: app-a\n    requires: [{ image: base-lib }]\n  - image: app-b\n    requires: [{ image: base-lib }]","handlingStrategy":"validation","validationCode":"// Go: detect cycles in artifact dependencies before running skaffold\nfunc hasCycle(artifacts map[string][]string) bool {\n\tvar visit func(node string, path map[string]bool) bool\n\tvisit = func(node string, path map[string]bool) bool {\n\t\tif path[node] {\n\t\t\treturn true\n\t\t}\n\t\tpath[node] = true\n\t\tfor _, dep := range artifacts[node] {\n\t\t\tif visit(dep, path) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\tdelete(path, node)\n\t\treturn false\n\t}\n\tfor n := range artifacts {\n\t\tif visit(n, map[string]bool{}) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"if hasCycle(depGraph) {\n\treturn errors.New(\"build dependencies must form a DAG; remove the cycle before running skaffold\")\n}","preventionTips":["Draw the artifact dependency graph when adding new requires entries","Never let an artifact depend (directly or transitively) on itself","Extract shared components into a dedicated base artifact to break mutual dependencies"],"tags":["skaffold","build-dependencies","cycle","config-validation"],"backgroundTag":"cyclic-dependency-detected","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"}