Skip to main content
DevOps Insightsguides & answers

Terraform · troubleshooting

Error: Cycle

Two or more things in the configuration each need the other to exist first, so Terraform cannot order them. The error lists every object in the loop; break one link in it.

Reproduced on Terraform 1.14.0, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 8 sections

Before you start

  • The full error line: every name it lists is part of the loop.

Run this first

step 1 of 1
terraform validate

Look for Every name in the list: the loop runs through all of them

Reports the cycle without credentials or state.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

What kind of names does the cycle list?

Cause space

3 of 3 still possible

  • Two resources reference each otherCommon
  • Modules pass values to each other in a loopOccasional
  • depends_on closes a loopOccasional

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    terraform validate

    Reports the cycle without credentials or state.

    Look for Every name in the list: the loop runs through all of them

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

Two resources reference each other

Common

The classic case is two security groups whose inline rules allow traffic from each other: each group needs the other's ID before it can be created.

Confirm

terraform validate

Exactly two resource names in the cycle

Fix

  • Create the groups without rules that point at each other, and put each rule in its own resource that references both groups. For AWS, the provider docs recommend aws_vpc_security_group_ingress_rule and aws_vpc_security_group_egress_rule over inline rules anyway.
resource "aws_vpc_security_group_ingress_rule" "db_from_web" {
  security_group_id            = aws_security_group.db.id
  referenced_security_group_id = aws_security_group.web.id
  ip_protocol                  = "tcp"
  from_port                    = 5432
  to_port                      = 5432
}

The same shape with terraform_data stand-ins validated on Terraform 1.14.0; argument names from the AWS provider docs, not run against AWS here.

Modules pass values to each other in a loop

Occasional

Module A takes an input from B's output, and B's output is computed from an input that comes from A.

Confirm

terraform validate

module.X.output and module.Y.var names in the cycle

Fix

  • Move the shared value out of both modules: create it in the root (or a third module) and pass it to both.

depends_on closes a loop

Occasional

An explicit depends_on added to fix an ordering problem points back at something that already depends on this resource.

Confirm

grep -rn depends_on --include='*.tf' .

A depends_on naming a resource listed in the cycle

Fix

  • Remove the depends_on, or narrow it; references already create the ordering Terraform needs.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

Terraform builds things in the order their references require: if A uses B's ID, B comes first. If B also uses A's ID, there is no valid order. The usual fix is to create both things plainly, then connect them with a third resource that refers to both.

Words used on this page

Dependency graph
The order Terraform derives from references and depends_on; it must have no loops.
(expand)
Appears on module, variable and output names in the cycle list; read past it to the names.
What is actually happening

Reproduced on 23 October 2026 with Terraform 1.14.0, using terraform_data resources as stand-ins so no provider was needed. Two resources that each referenced the other's ID failed terraform validate with Error: Cycle: terraform_data.db_sg, terraform_data.web_sg, exit 1. A loop made only of depends_on failed the same way.

Modules that feed each other are not automatically a cycle. Two modules passing each other an output passed validation when those outputs did not depend on the module's input, and failed with a cycle through module.a.output.echo and module.b.var.in when they did.

terraform graph failed with the same error, so the list in the message is the map: follow the names in it to find the link to remove.

How to tell this is your problem
WhereWhat you see
terraform validate, plan or applyError: Cycle: aws_security_group.db, aws_security_group.web
Across modulesError: Cycle: module.a.output.x (expand), module.b.var.in (expand), …

How to know it is actually fixed

  • terraform validate succeeds, and plan shows the resources and their connecting rules.

Stopping it happening again

  • Model connections between things as their own resources (rules, attachments, associations) rather than as arguments on either side.
  • Keep depends_on for ordering Terraform cannot see from references, and nothing else.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it