Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for OpenTofu By James Joyner IV · · 8 min read

OpenTofu Error: 'Cycle' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix OpenTofu 'Error: Cycle' dependency loops from circular resource references, mutual module dependencies, and depends_on chains.

  • #opentofu
  • #iac
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

OpenTofu builds a directed acyclic graph of your resources and evaluates it in dependency order. If two or more resources depend on each other — directly or through a chain — there is no valid order, so OpenTofu reports a cycle:

Error: Cycle: aws_security_group.app, aws_security_group.db

A longer form lists every node in the loop:

Error: Cycle: aws_security_group_rule.app_to_db,
aws_security_group.db, aws_security_group_rule.db_to_app,
aws_security_group.app

Symptoms

  • tofu plan/apply fails with Error: Cycle: followed by a comma-separated list of resource addresses.
  • The listed resources reference each other, forming a loop.
  • Common with security groups that reference each other, or modules that consume each other’s outputs.
  • Adding a depends_on sometimes triggers it where there was none before.

Common Root Causes

  • Mutual security-group references — group A allows B and group B allows A via inline rules.
  • Circular module dependencies — module X uses an output of Y, and Y uses an output of X.
  • depends_on loops — explicit dependencies that point back at an ancestor.
  • Self-referential attributes — a resource referencing its own computed value.
  • Over-broad depends_on on a whole module pulling in a resource that depends back.

How to diagnose

Read the cycle members — the fix is almost always to break one edge:

tofu plan 2>&1 | sed -n '1,10p'

Visualize the dependency graph to see the loop:

tofu graph | dot -Tsvg > graph.svg
tofu graph | grep -i security_group

Trace which attribute creates each edge in the loop:

grep -rn 'aws_security_group\.' *.tf

Fixes

Break mutual security-group references by moving the rules into standalone aws_security_group_rule (or vpc_security_group_ingress_rule) resources instead of inline blocks:

resource "aws_security_group" "app" {}
resource "aws_security_group" "db"  {}

resource "aws_vpc_security_group_ingress_rule" "app_to_db" {
  security_group_id            = aws_security_group.db.id
  referenced_security_group_id = aws_security_group.app.id
  ip_protocol                  = "tcp"
  from_port                    = 5432
  to_port                      = 5432
}

Standalone rule resources depend on the groups, but the groups no longer depend on each other — the cycle disappears.

Break circular module dependencies by extracting the shared resource into a third module both consume, so the arrows point one way.

Remove unnecessary depends_on — most dependencies are inferred from references; explicit depends_on back at an ancestor creates loops.

What to watch out for

  • Prefer standalone rule resources over inline ingress/egress blocks whenever two groups reference each other.
  • depends_on should point forward to things a resource genuinely needs; never at something downstream of it.
  • Refactor bidirectional module coupling into a shared lower-level module rather than passing outputs both ways.
  • tofu graph | dot is the fastest way to see the loop when the address list is long.
Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.