Skip to content

How to Fix Go Import Cycle Not Allowed

DodaTech 2 min read

In this tutorial, you'll learn about How to Fix Go Import Cycle Not Allowed. We cover key concepts, practical examples, and best practices.

The Problem

You compile your Go project and get:

package myapp/users
    imports myapp/orders
    imports myapp/users
import cycle not allowed

Package users imports orders, which imports users back. Go does not allow circular imports. The compiler refuses to build until the cycle is broken.

Quick Fix

Step 1: Identify the cycle

go mod graph | grep "myapp/users" | head -10

This shows which packages import users and which packages users imports. Follow the chain to find the cycle.

Step 2: Extract shared types into a separate package

Move common types that both packages need into a new types or models package:

// models/user.go
package models

type User struct {
    ID   int
    Name string
}
// models/order.go
package models

type Order struct {
    ID     int
    UserID int
    Total  float64
}

Both users and orders now import models instead of each other.

Step 3: Use interfaces instead of concrete types

Define an interface in the importing package:

// users/service.go
package users

type UserService interface {
    GetUser(id int) (*User, error)
}

The orders package depends on the interface, not the concrete implementation:

// orders/handler.go
package orders

type Handler struct {
    users UserService
}

The concrete implementation lives in users and does not import orders.

Step 4: Merge the packages

If the packages are tightly coupled, they should be a single package:

package users

// User and Order types both live here

This is a pragmatic solution when the packages are logically inseparable.

Step 5: Use a service layer as mediator

Create an intermediate package that both packages import:

// service/service.go
package service

import (
    "myapp/models"
    "myapp/users"
    "myapp/orders"
)

func PlaceOrder(userID int, productID int) (*models.Order, error) {
    user, _ := users.GetUser(userID)
    return orders.CreateOrder(user, productID)
}

Step 6: Verify the cycle is broken

go build ./...

Expected:

(no output, build succeeds)

Step 7: Visualize the dependency graph

go install golang.org/x/tools/cmd/deadcode@latest
deadcode ./...

Dead code detection can also reveal unnecessary imports that lead to cycles.

Alternative Solutions

Restructure your project following the standard Go project layout with clear separation between internal/, pkg/, and cmd/ directories.

Use dependency injection to invert the import direction. Instead of users importing orders, have users accept an OrderService interface defined in a shared package.

Prevention

  • Design packages with a clear dependency direction (domain -> application -> infrastructure).
  • Keep packages small and focused on a single responsibility.
  • Run go vet as part of CI to catch cycle-related issues early.
  • Review import statements during code reviews to prevent cycles from forming.
  • Use go mod graph | dot -Tpng > deps.png to visualize the dependency graph periodically.
  • Add a go vet ./... check in your CI pipeline to catch import cycles before merge.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro