{"record":{"id":"ea7f34c430c6c0fa","repo":"hashicorp/terraform","slug":"self-reference-s","errorCode":null,"errorMessage":"Self reference: %s","messagePattern":"Self reference: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dag/dag.go","lineNumber":247,"sourceCode":"\t\t\t\t\tfirst = i\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// pivot the slice around our new first index\n\t\t\tif first > 0 {\n\t\t\t\tcycleStr = append(cycleStr[first:], cycleStr[:first]...)\n\t\t\t}\n\n\t\t\terr = errors.Join(err, fmt.Errorf(\n\t\t\t\t\"Cycle:\\n  %s\", strings.Join(cycleStr, \"\\n  \"),\n\t\t\t))\n\t\t}\n\t}\n\n\t// Look for cycles to self\n\tfor _, e := range g.Edges() {\n\t\tif e.Source == e.Target {\n\t\t\terr = errors.Join(err, fmt.Errorf(\n\t\t\t\t\"Self reference: %s\", e.Source.Name()))\n\t\t}\n\t}\n\n\treturn err\n}\n\n// Cycles reports any cycles between graph nodes.\n// Self-referencing nodes are not reported, and must be detected separately.\nfunc (g *AcyclicGraph) Cycles() [][]Vertex {\n\tvar cycles [][]Vertex\n\tfor _, cycle := range StronglyConnected(&g.Graph) {\n\t\tif len(cycle) > 1 {\n\t\t\tcycles = append(cycles, cycle)\n\t\t}\n\t}\n\treturn cycles\n}","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/dag/dag.go#L229-L265","documentation":"Returned by AcyclicGraph.Validate when an edge has the same Source and Target — a self-loop. Unlike multi-vertex cycles (handled by [857]), self-loops are detected by a separate scan after the SCC pass because StronglyConnected excludes single-vertex trivial components. Each self-referencing vertex is reported by name.","triggerScenarios":"A graph edge whose Source and Target are the same Vertex is added (graph.Connect or Dagger edge insertion), then AcyclicGraph.Validate() is called. In Terraform this typically means a resource somehow lists itself in depends_on or an internal transformer wires a node to itself.","commonSituations":"Generated configs that include a resource in their own depends_on (often via templating that interpolates the resource's own address); internal graph transformers with off-by-one bugs wiring a node to itself; module self-includes.","solutions":["Find the resource named in the error and remove the self-reference in its depends_on (or equivalent dependency declaration).","If generated by a template, fix the template so it does not emit a resource's own address as a dependency.","For internal graph builders, guard edge insertion against Source == Target.","Re-run 'terraform plan' to confirm the self-reference is resolved."],"exampleFix":"// before\nresource \"a\" \"x\" {\n  depends_on = [a.x]\n}\n\n// after\nresource \"a\" \"x\" {}","handlingStrategy":"validation","validationCode":"func hasNoSelfEdge(g *dag.AcyclicGraph) bool {\n    for _, e := range g.Edges() {\n        if e.Source == e.Target { return false }\n    }\n    return true\n}","typeGuard":"func hasNoSelfEdge(g *dag.AcyclicGraph) bool {\n    for _, e := range g.Edges() {\n        if e.Source == e.Target { return false }\n    }\n    return true\n}","tryCatchPattern":null,"preventionTips":["Never list a resource's own address in its depends_on.","When generating configs from templates, exclude the resource's own address from its dependency list.","For internal graph builders, guard Connect against Source == Target."],"tags":["dag","self-reference","dependency-graph","terraform-core","go"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}