docker/compose · error

cycle found: %s

Error message

cycle found: %s

What it means

The dependency graph DFS (Graph.visit) found a cycle in depends_on (and other dependency edges) and reports the exact path, e.g. 'web -> api -> web'. Compose cannot determine startup order for cyclic dependencies and aborts before creating anything.

Source

Thrown at pkg/compose/dependencies.go:431

		if !slices.Contains(discovered, vertex.Key) && !slices.Contains(finished, vertex.Key) {
			var err error
			discovered, finished, err = g.visit(vertex.Key, path, discovered, finished)
			if err != nil {
				return true, err
			}
		}
	}

	return false, nil
}

func (g *Graph) visit(key string, path []string, discovered []string, finished []string) ([]string, []string, error) {
	discovered = append(discovered, key)

	for _, v := range g.Vertices[key].Children {
		path := append(path, v.Key)
		if slices.Contains(discovered, v.Key) {
			return nil, nil, fmt.Errorf("cycle found: %s", strings.Join(path, " -> "))
		}

		if !slices.Contains(finished, v.Key) {
			if _, _, err := g.visit(v.Key, path, discovered, finished); err != nil {
				return nil, nil, err
			}
		}
	}

	discovered = slices.DeleteFunc(discovered, func(s string) bool { return s == key })
	finished = append(finished, key)
	return discovered, finished, nil
}

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Break the loop: make one direction optional (required: false) or drop it
  2. Restructure so shared prerequisites are a third service both depend on
  3. For mutual readiness, use healthchecks + restart-on-failure instead of circular depends_on
  4. docker compose config and inspect depends_on blocks to spot the back-edge

Example fix

# before
services:
  a:
    depends_on: [b]
  b:
    depends_on: [a]
# after
services:
  a:
    depends_on: [b]
  b: {}
Defensive patterns

Strategy: validation

Validate before calling

// DFS cycle check over depends_on before calling compose
var visit func(string, map[string]bool) error
visit = func(n string, seen map[string]bool) error {
    if seen[n] { return fmt.Errorf("cycle at %s", n) }
    seen[n] = true
    for dep := range project.Services[n].DependsOn {
        if err := visit(dep, seen); err != nil { return err }
    }
    delete(seen, n)
    return nil
}
for n := range project.Services { if err := visit(n, map[string]bool{}); err != nil { return err } }

Prevention

When it happens

Trigger: Service A depends_on B while B depends_on A; longer loops a->b->c->a; also cycles introduced via multiple edges (depends_on plus other implicit deps) during HasCycles after graph build.

Common situations: Two services each waiting on the other (db healthcheck gating app while app is db's dependency); refactoring that inverts a dependency accidentally; healthcheck condition loops.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/497c8c05f8dbdcac. Report an issue: GitHub.