go mod init はモジュール(Goのコードをまとめる単位)を作って go.mod を生成するコマンド、go mod tidy はソースコードの import に合わせて go.mod と go.sum を過不足なく整えるコマンドです。 go.mod には「このモジュールの名前と、依存するモジュールの版」が、go.sum には「ダウンロードした内容が前回と同じかを確かめるためのハッシュ」が記録されます。

この記事では、init → get → tidy → 更新 → 削除(go get pkg@none)の流れを、各段階の go.mod / go.sum の中身つきで順に見ていきます。あわせて、コマンド早見表、go.mod のディレクティブ一覧、よく出るエラーの原文と対処もまとめました。出力はすべて Go 1.24.7(linux/amd64)で実行した結果です。

検証環境は外部ネットワークに出られないため、確認用に自作した3つのモジュール(example.com/greet、example.com/textutil、example.com/tools)をローカルのプロキシ(GOPROXY=file://...、GOSUMDB=off)に置いて実行しました。github.com/... などの公開モジュールでも、コマンドと go.mod の変化のしかたは同じです。出力中のパスは /home/taro/... に置き換えています(表示は環境に合わせて置き換えています)。

go modの早見表

コマンド早見表

やりたいこと コマンド 変わるファイル
モジュールを作る go mod init example.com/hello go.mod を作成
import に合わせて整える go mod tidy go.mod、go.sum
整えたら何が変わるか見るだけ go mod tidy -diff 変更なし(差分を表示)
パッケージを追加する go get example.com/greet go.mod、go.sum
版を指定して追加・変更する go get example.com/greet@v1.1.0 go.mod、go.sum
最新にする go get example.com/greet@latest go.mod、go.sum
依存先も含めて更新する go get -u example.com/greet go.mod、go.sum
パッケージを削除する go get example.com/greet@none go.mod
ツールを追加する(Go 1.24〜) go get -tool パッケージのパス go.mod に tool 行
依存を先にダウンロードする go mod download go.sum(無ければ作成)
なぜ必要かを調べる go mod why example.com/textutil 変更なし
依存関係のグラフを出す go mod graph 変更なし
依存の一覧と更新の有無を見る go list -m -u all 変更なし
公開されている版を一覧する go list -m -versions example.com/greet 変更なし
キャッシュの内容を検証する go mod verify 変更なし
依存を vendor ディレクトリへコピーする go mod vendor vendor/ を作成
go.mod をコマンドで編集する go mod edit -replace 旧=新 など go.mod
複数モジュールをまとめて開発する go work init ./a ./b go.work を作成

go.modとgo.sumの違い

比べる点 go.mod go.sum
役割 モジュール名と、依存するモジュールの版を宣言する ダウンロードした内容のハッシュを記録する
中身の例 require example.com/greet v1.2.0 example.com/greet v1.2.0 h1:vx6q...=
手で編集するか することもある(go mod edit が安全) しない(コマンドに任せる)
Gitに入れるか 入れる 入れる
無いとどうなるか モジュールとして扱われない missing go.sum entry で止まる

手順1:go mod initでモジュールを作る

mkdir hello && cd hello
go mod init example.com/hello
cat go.mod

実行結果:

go: creating new go.mod: module example.com/hello
module example.com/hello

go 1.24.7

引数の example.com/hello がモジュールパス(モジュールの名前)です。 公開する予定があるなら github.com/ユーザー名/リポジトリ名 のように取得元と同じ形にします。手元だけで使うなら example.com/hello のような名前で構いません。go の行には、使っている Go の版が自動で入ります。

次に、外部のパッケージを import する main.go を用意します。

package main

import (
    "fmt"

    "example.com/greet"
)

func main() {
    fmt.Println(greet.Hello("gopher"))
}

この時点で go run . を実行すると、依存がまだ登録されていないことを教えてくれます。

main.go:6:2: no required module provides package example.com/greet; to add it:
    go get example.com/greet

手順2:go getでパッケージを追加する

go get example.com/greet@v1.0.0
go: downloading example.com/greet v1.0.0
go: added example.com/greet v1.0.0

go.mod(追加後):

module example.com/hello

go 1.24.7

require example.com/greet v1.0.0 // indirect

go.sum(新しく作られる):

example.com/greet v1.0.0 h1:OESONE+El+COGRCcyP3DBItsRznDe/wycz2LaNzQC9Y=
example.com/greet v1.0.0/go.mod h1:rcTgdhGf2dvXrNywJG0jkV+0342RC7PiKN0YX33Ej6Y=
go run .
Hello, gopher! (greet v1.0.0)

ここで気づく点が2つあります。

  • main.go で直接 import しているのに // indirect が付いています。 go get 単体では import の有無までは整理されません。この印は次の go mod tidy で正しく付け直されます。
  • go.sum は1つの版につき2行です。1行目がモジュール全体(zip)のハッシュ、/go.mod が付いた2行目が go.mod ファイルだけのハッシュです。

@v1.0.0 を省略して go get example.com/greet とすると、最新の版が選ばれます。

手順3:バージョンを指定して更新する

公開されている版は go list -m -versions で確認できます。

go list -m -versions example.com/greet
example.com/greet v1.0.0 v1.1.0 v1.2.0

v1.1.0 に上げてみます。v1.1.0 は内部で example.com/textutil を使っています。

go get example.com/greet@v1.1.0
go: downloading example.com/greet v1.1.0
go: downloading example.com/textutil v0.3.0
go: upgraded example.com/greet v1.0.0 => v1.1.0
go: added example.com/textutil v0.3.0

go.mod(差分):

-require example.com/greet v1.0.0 // indirect
+require (
+   example.com/greet v1.1.0 // indirect
+   example.com/textutil v0.3.0 // indirect
+)

go.sum には v1.1.0 と textutil v0.3.0 の行が追記され、v1.0.0 の行もそのまま残ります(この時点で6行)。

更新できる版があるかは go list -m -u all で一覧できます。

go list -m -u all
example.com/hello
example.com/greet v1.1.0 [v1.2.0]
example.com/textutil v0.3.0 [v0.4.0]

角かっこの中が、更新できる版です。 -u を付けた go get は、指定したモジュールに加えて、その依存先も新しい版に上げます。

go get -u example.com/greet
go: downloading example.com/greet v1.2.0
go: downloading example.com/textutil v0.4.0
go: upgraded example.com/greet v1.1.0 => v1.2.0
go: upgraded example.com/textutil v0.3.0 => v0.4.0
go run .
Hello, GOPHER! (greet v1.2.0 / textutil v0.4.0)
書き方 動き 今回の結果
go get pkg@v1.1.0 その版にする(上げる・下げるどちらも可) greet v1.0.0 → v1.1.0
go get pkg@latest そのモジュールだけ最新にする greet → v1.2.0(textutil はそのまま)
go get -u pkg そのモジュールと依存先を新しい版にする greet → v1.2.0、textutil → v0.4.0
go get -u ./... 自分のコードが使う依存をまとめて新しくする (未実行)
go get -u=patch pkg パッチ版の範囲だけ上げる (未実行)

版を下げることもできます。go get example.com/greet@v1.1.0 を実行すると、次のように表示されました。

go: downgraded example.com/greet v1.2.0 => v1.1.0

手順4:go mod tidyで整える

ここまでで go.mod は次の状態です(// indirect が実態と合っておらず、go.sum には使わなくなった版の行が残っています)。

require (
    example.com/greet v1.2.0 // indirect
    example.com/textutil v0.4.0 // indirect
)
go mod tidy

go.mod(tidy 後):

module example.com/hello

go 1.24.7

require example.com/greet v1.2.0

require example.com/textutil v0.4.0 // indirect

go.sum(tidy 後。10行から4行に):

example.com/greet v1.2.0 h1:vx6qzHG2xYogOpPCKdm01yk4afFGwDIO2TlnU7Nz4eg=
example.com/greet v1.2.0/go.mod h1:LNpKFOQstE7A4doP31zjjod9pCtsQi5CXgTnKQhatj0=
example.com/textutil v0.4.0 h1:4b1Jham+WkbbY4kSG3XVJQwBOFy/dIlL7/mmX1CSGV4=
example.com/textutil v0.4.0/go.mod h1:qypvavMD4Fr08zDyhe/hKxUFqGubmHfq1kovmYR5Xqw=

tidy がしてくれたことは3つです。

tidy の働き 今回の変化
直接 import しているものから // indirect を外す greet が1つ目の require ブロックへ
間接的な依存を別ブロックに分ける textutil が2つ目のブロックへ(// indirect 付き)
使っていない版の行を go.sum から除く v1.0.0・v1.1.0・textutil v0.3.0 の行が消えた

// indirect は、「自分のコードは直接 import していないが、依存先が必要としているモジュール」という印です(意味は go.dev の Go Modules Reference で確認しました)。

tidyは「import を書いてから実行」でも使える

go get を使わず、ソースに import を書いてから go mod tidy を実行する流れでも依存を追加できます。require が無い状態から実行した結果です。

go: finding module for package example.com/greet
go: found example.com/greet in example.com/greet v1.2.0
require example.com/greet v1.2.0

require example.com/textutil v0.3.0 // indirect

直接 import した greet は最新の v1.2.0、間接的な textutil は greet が要求している v0.3.0 になりました(最新の v0.4.0 ではありません)。Go は「必要とされている版のうち最小限のもの」を選ぶ方式のため、間接的な依存は勝手に最新へは上がりません。上げたいときは go get -u か、版を指定した go get を使います。

変更前に差分だけ確認する(tidy -diff)

-diff を付けると、ファイルを書き換えずに差分を表示します。main.go から greet の import を消した状態で実行した結果です。

go mod tidy -diff
diff current/go.mod tidy/go.mod
--- current/go.mod
+++ tidy/go.mod
@@ -1,7 +1,3 @@
 module example.com/hello

 go 1.24.7
-
-require example.com/greet v1.2.0
-
-require example.com/textutil v0.4.0 // indirect

(続けて go.sum の差分も表示されます。)

手順5:パッケージを削除する(go get pkg@none)

依存を外す方法は2つあります。

方法 手順 向いている場面
A. import を消して go mod tidy ソースから import を削除 → tidy 普段はこちら。go.sum まで一度に片付く
B. go get pkg@none コマンドで require を外す 特定のモジュールを明示的に外したいとき

B を、import を消したあとに実行した結果です。

go get example.com/greet@none
go: removed example.com/greet v1.2.0

go.mod(実行後):

module example.com/hello

go 1.24.7

require example.com/textutil v0.4.0 // indirect

greet の行は消えましたが、間接的な依存だった textutil の行と go.sum の中身は残りました。 続けて go mod tidy を実行すると、go.mod は module と go の2行だけになり、go.sum はファイルごと無くなりました。

ソースにまだ import が残っている状態で @none を実行した場合は、require だけが消えるため、ビルドで次のように表示されます。

main.go:6:2: no required module provides package example.com/greet; to add it:
    go get example.com/greet

「import を消す → go mod tidy」の順にすると、この行き違いが起きません。@none を使ったあとも、最後に tidy で仕上げるのがおすすめです。

go.modのディレクティブ一覧

ディレクティブ 書き方の例 意味
module module example.com/hello このモジュールのパス(名前)。import するときの先頭部分になる
go go 1.24.7 このモジュールが必要とする Go の最小の版。使える言語機能にも影響する
toolchain toolchain go1.24.7 使う Go ツールチェーンの提案(Go 1.21〜)。今回は未実行
require require example.com/greet v1.2.0 依存するモジュールと、その最小の版
// indirect require example.com/textutil v0.4.0 // indirect 自分のコードからは直接 import していない依存に付くコメント
replace replace example.com/greet => ../greet-local モジュールの中身を別の場所や版に差し替える(メインのモジュールでだけ有効)
exclude exclude example.com/textutil v0.4.0 その版を読み込み対象から外す
tool tool example.com/tools/cmd/hellotool go tool で実行できるツールとして登録する(Go 1.24〜)
retract retract v1.0.1 モジュールの作者が、公開済みの版を取り下げる印(今回は未実行)
godebug godebug default=go1.21 メインのモジュールに適用する GODEBUG 設定(Go 1.24〜の記載。今回は未実行)

各ディレクティブの意味は、今回 go.dev の Go Modules Reference を開いて確認しました。

replaceで手元のコードに差し替える

依存先のモジュールを手元で直しながら試したいときに使います。../greet-local に同じモジュールパス(module example.com/greet)の go.mod と修正版のコードを置いた状態で実行しました。

go mod edit -replace example.com/greet=../greet-local
go run .
Hello, gopher! (手元で修正中のgreet)

go.mod には次の1行が加わります。

replace example.com/greet => ../greet-local

go list -m all でも差し替えが見えます。

example.com/hello
example.com/greet v1.2.0 => ../greet-local

元に戻すときは go mod edit -dropreplace example.com/greet のあと go mod tidy を実行します。replace は自分のモジュールをビルドするときだけ効き、他の人があなたのモジュールを依存として使うときには無視されるので、手元での確認が終わったら外しておくと混乱がありません。

toolディレクティブでツールの版をそろえる(Go 1.24〜)

コード生成やリンターなど、開発時に使うコマンドの版を go.mod で管理できます。

go get -tool example.com/tools/cmd/hellotool@v0.1.0
go: downloading example.com/tools v0.1.0
go: added example.com/tools v0.1.0

go.mod(実行後):

module example.com/hello

go 1.24.7

require example.com/greet v1.2.0

require (
    example.com/textutil v0.3.0 // indirect
    example.com/tools v0.1.0 // indirect
)

tool example.com/tools/cmd/hellotool

登録したツールは go tool 名前 で実行できます。

go tool hellotool a b
hellotool v0.1.0 args: [a b]

引数なしの go tool を実行すると、標準のツール(vet など)の一覧の末尾に example.com/tools/cmd/hellotool が並びました。tool 行がある間は、go mod tidy を実行しても tools の require は消えません。 外すときは go get -tool example.com/tools/cmd/hellotool@none で tool 行を消し、続けて go mod tidy を実行します。

依存を調べるコマンド(why・graph・verify・download)

go mod why example.com/textutil
# example.com/textutil
example.com/hello
example.com/greet
example.com/textutil

上から順に「hello が greet を import し、greet が textutil を import している」と読みます。身に覚えのない依存が go.mod にあるとき、どこから来たのかがわかります。

go mod graph
example.com/hello example.com/greet@v1.2.0
example.com/hello example.com/textutil@v0.4.0
example.com/hello go@1.24.7
example.com/greet@v1.2.0 example.com/textutil@v0.3.0
example.com/greet@v1.2.0 go@1.21
example.com/textutil@v0.4.0 go@1.21
go@1.24.7 toolchain@go1.24.7

1行が「左が右を要求している」という関係です。greet 自身は textutil v0.3.0 を要求していますが、hello が v0.4.0 を要求しているため、ビルドには v0.4.0 が使われます。

go mod verify
all modules verified

go mod download は、ビルドの前に依存をまとめて取得しておくコマンドです。CI や Docker のビルドで、ソースをコピーする前に go.mod と go.sum だけで実行しておくと、キャッシュが効きやすくなります(Docker でのビルドは今回未実行です)。

vendor・go mod edit・go workの使いどころ

go mod vendor:依存をリポジトリの中に置く

go mod vendor
find vendor -type f
vendor/example.com/greet/greet.go
vendor/example.com/textutil/textutil.go
vendor/modules.txt

vendor/modules.txt には、コピーしたモジュールと版が記録されます。

# example.com/greet v1.2.0
## explicit; go 1.21
example.com/greet
# example.com/textutil v0.3.0
## explicit; go 1.21
example.com/textutil

vendor ディレクトリがある状態で go build ./... が通ることを確認しました。ネットワークに出られない環境でビルドしたいときに便利です。

go mod edit:スクリプトからgo.modを書き換える

コマンド 働き
go mod edit -go=1.23 go 行を書き換える
go mod edit -require=example.com/textutil@v0.3.0 require を追加する
go mod edit -droprequire=example.com/textutil require を削除する
go mod edit -replace 旧=新 / -dropreplace 旧 replace を追加・削除する
go mod edit -exclude=example.com/textutil@v0.4.0 exclude を追加する
go mod edit -print 現在の go.mod を表示する
go mod edit -json JSON 形式で表示する

表のコマンドはすべて実行して、go.mod が期待どおり変わることを確認しました。edit は go.mod の文字を書き換えるだけで、ダウンロードや整合性の確認はしません。 実行後は go mod tidy で整えます。

go work:複数のモジュールを同時に開発する

replace を go.mod に書かずに、手元の複数モジュールをまとめて扱う仕組みです。

go work init ./hello ./greet-local
cat go.work
go 1.24.7

use (
    ./greet-local
    ./hello
)

この状態で hello の中から go run . を実行すると、go.mod には replace が無いのに手元の greet が使われました。

Hello, gopher! (手元で修正中のgreet)

GOWORK=off go run . とすると、go.work を無視して通常どおり v1.2.0 が使われます。

比べる点 replace go work
書く場所 go.mod go.work(モジュールの外)
向いている場面 一時的に1つ差し替える 複数モジュールを継続して同時に開発する
共有するか go.mod に残るので注意が必要 go.work は手元専用にすることが多い

よく出るエラーと対処

実際に再現したメッセージの原文です(Go 1.24.7)。

no required module provides package

main.go:6:2: no required module provides package example.com/greet; to add it:
    go get example.com/greet

原因:import しているパッケージが go.mod の require に無い。

対処:表示されている go get ... を実行するか、go mod tidy を実行します。

missing go.sum entry

go.sum を削除した状態で go build ./... を実行すると表示されました。

main.go:6:2: missing go.sum entry for module providing package example.com/greet (imported by example.com/hello); to add:
    go get example.com/hello

原因:go.mod には require があるのに、対応する行が go.sum に無い(go.sum をコミットし忘れた、手で消した、など)。

対処:go mod tidy を実行します。今回は go mod download でも go.sum が作り直され、ビルドが通りました。

module declares its path as ... but was required as ...

go: example.com/oldname@v1.0.0 requires example.com/oldname@v1.0.0: parsing go.mod:
    module declares its path as: example.com/newname
            but was required as: example.com/oldname

原因:取得しようとしたパスと、そのモジュールの go.mod の module 行に書かれた名前が違う。リポジトリの移転や改名のあと、fork したリポジトリを直接指定したときによく見られます。

対処:declares its path as に表示された名前(ここでは example.com/newname)で import と go get をやり直します。fork を使いたい場合は、元の名前のまま require し、replace で fork に差し替えます。

updates to go.mod needed

go.mod から必要な require を1行消した状態でビルドすると表示されました。

go: updates to go.mod needed; to update it:
    go mod tidy

対処:メッセージのとおり go mod tidy を実行します。

checksum mismatch(SECURITY ERROR)

go.sum のハッシュを1文字書き換えてビルドすると表示されました。

verifying example.com/greet@v1.2.0: checksum mismatch
    downloaded: h1:vx6qzHG2xYogOpPCKdm01yk4afFGwDIO2TlnU7Nz4eg=
    go.sum:     h1:Xx6qzHG2xYogOpPCKdm01yk4afFGwDIO2TlnU7Nz4eg=

SECURITY ERROR
This download does NOT match an earlier download recorded in go.sum.

原因:ダウンロードした内容と go.sum の記録が一致しない。

対処:まず go.sum を手で編集していないか、マージで行が崩れていないかを確認します。go.sum をバージョン管理の状態に戻すとビルドが通りました。同じ版の中身が後から差し替えられた場合にもこの表示になるため、心当たりが無いときは取得元を確かめます。

go.mod file not found

go: go.mod file not found in current directory or any parent directory; see 'go help modules'

対処:モジュールのディレクトリへ移動するか、go mod init モジュールパス を実行します。

go mod init まわりの2つ

引数なしで実行したとき:

go: cannot determine module path for source directory /home/taro/empty (outside GOPATH, module path must be specified)

Example usage:
    'go mod init example.com/m' to initialize a v0 or v1 module
    'go mod init example.com/m/v2' to initialize a v2 module

すでに go.mod があるとき:

go: /home/taro/empty/go.mod already exists

エラーの逆引き表

メッセージの要点 原因 まず試すこと
no required module provides package require に無い go get パッケージ または go mod tidy
missing go.sum entry go.sum に行が無い go mod tidy
module declares its path as パスと module 行の名前が違う 表示された名前で取得する/replace を使う
updates to go.mod needed go.mod の内容が足りない go mod tidy
checksum mismatch go.sum と内容が一致しない go.sum を元の状態に戻して原因を確認
go.mod file not found モジュールの外で実行した ディレクトリを確認/go mod init
cannot determine module path init に引数が無い go mod init example.com/名前
go.mod already exists すでに初期化済み そのまま使う

よくある質問

Q. go.sum はGitにコミットしますか? A. コミットします。go.mod と go.sum の両方がそろっていると、ほかの人やCIでも同じ内容の依存でビルドできます。go.sum が無い状態では missing go.sum entry と表示されることを確認しました。

Q. go get と go mod tidy はどう使い分けますか? A. 版を指定して追加・更新・削除したいときは go get、import の状態に合わせて全体を整えたいときは go mod tidy です。go get だけでは // indirect の付き方や go.sum の不要な行が残るため、最後に tidy を実行しておくと整います。

Q. go.mod に書かれた // indirect は消してよいですか? A. 手で消す必要はありません。直接 import していない依存に自動で付く印で、go mod tidy が実態に合わせて付けたり外したりします。

Q. go install と go get の違いは何ですか? A. go get は go.mod の依存を変更するコマンド、go install パッケージ@版 はコマンドをビルドしてインストールするコマンドです(go install は今回未実行)。モジュールごとにツールの版をそろえたい場合は、Go 1.24以降の go get -tool と go tool が使えます。

まとめ

  • go mod init モジュールパス で go.mod を作る。中身は module 行と go 行だけから始まります。
  • 追加は go get pkg、版の指定は @v1.2.3、最新は @latest、依存先ごと更新は -u。
  • 削除は「import を消して go mod tidy」が基本。go get pkg@none は require を明示的に外したいときに使い、最後に tidy で仕上げます。
  • go mod tidy は // indirect の整理と go.sum の掃除まで行う。変更前に -diff で確認できます。
  • go.mod は「何をどの版で使うか」、go.sum は「中身が同じかの確認用」。どちらもコミットします。
  • 手元の差し替えは replace、複数モジュールの同時開発は go work、ツールの版管理は tool ディレクティブ。
  • エラーの多くは go mod tidy で解消。module declares its path as は名前を、checksum mismatch は go.sum を確認します。

参考・出典

  • Go Modules Reference(go.dev/ref/mod)― ディレクティブの意味、// indirect、go.sum の形式、go get pkg@none、go mod tidy のフラグを確認
  • 本文の実行結果は Go 1.24.7(linux/amd64)で、自作の確認用モジュールをローカルプロキシ(GOPROXY=file://...、GOSUMDB=off)に置いて実行したものです。公開モジュールの取得、toolchain・retract・godebug ディレクティブ、go get -u ./...、go get -u=patch、go install は未実行です。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?