Go で「import cycle not allowed」が解決できない
Go はパッケージ同士が直接・間接に循環参照することを禁止している。
共通部分を第3のパッケージへ切り出すか、依存の向きをインターフェースで逆転させて循環を断ち切る。
公開: 更新:
要約
import cycle not allowed は、Go のパッケージが互いを直接または間接に import し合う「循環参照」になっているときのコンパイルエラーです。
Go 言語仕様はパッケージの循環 import を認めていません。
これは設計上の制約で、回避ではなく依存関係そのものを一方向に整理して解消します。
典型的には、共通部分を別パッケージに切り出すか、インターフェースで依存の向きを逆転させます。
package myapp/service
imports myapp/repo
imports myapp/service: import cycle not allowed実行例
golang:1.23 で同じ構成を組むと、go build ./... は循環している import の連なりをそのまま並べて示す。
インターフェースを呼び出し側に置いて依存を一方向にすると、同じコマンドが通り go run . の実行まで到達する。
$ go build ./...
package example.com/myapp
imports example.com/myapp/service
imports example.com/myapp/repo
imports example.com/myapp/service: import cycle not allowed
$ echo $?
1$ go list -deps ./service
package example.com/myapp/service
imports example.com/myapp/repo
imports example.com/myapp/service: import cycle not allowed
$ echo $?
1$ go build ./...
$ echo $?
0$ go run .
alice— 2026-09-09 時点の出力
検証環境
- 検証日
- 実行環境
golang:1.23Debian GNU/Linux 12 (bookworm)- バージョン
- Python 3.11.2
- Git 2.39.5
- Go 1.23.12
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
よくある原因
- 相互参照:
serviceがrepoを、repoがserviceを import している。 - 共通型の置き場所: 両方が使う型を一方のパッケージに置いたため、もう一方がそれを取りに行って循環する。
- ユーティリティの逆流: 「便利関数」パッケージが、本来上位であるはずのパッケージを import している。
- 場所ベースの分割: 責務ではなくファイルの置き場所でパッケージを切ったため、依存が双方向になっている。
解決策
1. 共通部分を第3のパッケージへ切り出す
A と B が共通で必要とする型・定数・インターフェースを model のような独立パッケージに移し、A も B も model を import する形にします。
依存の矢印が model に向かう一方向になり、循環が消えます。
変更前: service ⇄ repo
変更後: service → model ← repo2. インターフェースで依存を逆転させる
下位(repo)が上位(service)の型を直接使いたい場合は、repo 側に必要な振る舞いをインターフェースとして定義し、service がそれを実装します。
下位は上位を import しなくて済みます(依存性逆転)。
// repo パッケージ側でインターフェースを定義し、service を import しない
type Notifier interface {
Notify(msg string) error
}3. 統合・可視化で見直す
分ける必然性が薄ければ 1 パッケージにまとめるのも有効です。
複雑な依存は go list -deps ./... などで可視化し、どの辺が循環しているかを特定してから断ち切ります。