Skip to content
Branch: master
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Using go list, go mod why and go mod graph

go list, go mod why and go mod graph can be used to analyse modules and packages. In this guide we introduce each command as we analyse the module.

For any of the commands, go help provides more details; for example, go help list or go help mod graph. These help guides are also available on the Go website.


Clone our example repo ready for analysis:

$ git clone -q /home/gopher/scratchpad/listmodwhygraph
$ cd /home/gopher/scratchpad/listmodwhygraph

Our main module is:

$ go list -m

The -m flag to go list causes list to list modules instead of packages.

The packages in the main module are:

$ go list

That is to say, the module consists of the single package

The main module has the following structure:

$ tree --noreport
|-- go.mod
|-- go.sum
`-- listmodwhygraph.go

Examine the single .go file in the single package in our main module:

$ cat /home/gopher/scratchpad/listmodwhygraph/listmodwhygraph.go
package listmodwhygraph

import (
	_ ""
	_ ""
	_ ""

Ensure the main module's go.mod (and go.sum) reflects its source code:

$ go mod tidy

Building on "Visually analysing module dependencies", use go mod graph to look at the module requirement graph. Remember, each of the nodes in this graph is a module, and an edge m1 -> m2 indicates that m1 requires m2. Click the image for a larger version.

Module Dependency Graph

See below for details on how this graph was generated.

The nodes of this graph can also be given by:

$ go list -m all v1.1.1 v0.0.0-20190513150356-5cba17ec1a73

Looking at the graph and the output of go list, the first question that comes to mind is why the module appears to be a dependency of the main module, because it is not imported by hello.go.

We can answer that question using go mod why which shows us the shortest path from a package in the main module to any package in the module:

$ go mod why -m

This seems to suggest that the dependency exists because of the requirements of But the edge -> is definitely not visible on our graph.

Use go list again to find out more information about

$ go list -m -json
	"Path": "",
	"Version": "v0.0.0-20190511041617-99f201b6807e",
	"Time": "2019-05-10T21:16:17-07:00",
	"Indirect": true,
	"Dir": "/home/gopher/pkg/mod/",
	"GoMod": "/home/gopher/pkg/mod/cache/download/"

This shows that is an indirect dependency. Indirect requirements only arise when using modules that fail to state some of their own dependencies (this includes requirements that have not yet been converted to be modules) or when explicitly upgrading a module's dependencies ahead of its own stated requirements.

We can also see this from the // indirect annotation in the main module's go.mod:

$ cat /home/gopher/scratchpad/listmodwhygraph/go.mod

go 1.12

require ( v0.0.0-20190513150356-5cba17ec1a73 v0.1.0 v1.4.1 v0.0.0-20190511041617-99f201b6807e // indirect

In this case, the indirect dependency comes about because the go.mod for fails to completely state its dependencies; is missing.

Returning to go list.

go list gives information about packages. By default it lists the import path of packages matching the package patterns provided. With no arguments go list applies to the package in the current directory:

$ go list

We can extend this to include the transitive dependencies of the named packages:

$ go list -deps

Use the -f flag to exclude standard library dependencies:

$ go list -deps -f "{{if not .Standard}}{{.ImportPath}}{{end}}"

Looking at the original module requirement graph, we might ask ourselves, why are there no packages from the modules and in the go list output? Use go mod why -m to find the shortest path from a package in the main module to a package in each module.


$ go mod why -m

We see that the package in the module is one such path. We can therefore expect an identical answer if we ask the question of the package itself (no -m this time):

$ go mod why


$ go mod why -m
(main module does not need module

This tells us that there is no package from the module in the transitive import graph of of the main module (

So why does the appear in the module requirement graph? Currently, no command can give us the answer (something that is being tracked in #27900 and will likely be fixed in Go 1.12).

But we can clearly see the answer by visually inspecting the requirement graph above. We see that requires So why does exist in the requirement graph?

$ go mod why -m

For this module we have a concrete answer. So we are now clear that is in our requirement graph because it is transitively used by an import of our main package. Furthermore, is present because it is a requirement of and given the module requirement graph is conservative it reflects all such requirements.


go list, go mod graph and go mod why are very powerful tools when it comes to understanding your code and its dependencies. We have barely scratched the surface of go list in this simple guide; as with all tools, read the help for more information on each, for example. go help list.

Generating the module requirement graph

Generate the module requirement graph:

$ go mod graph | sed -Ee 's/@[^[:blank:]]+//g' | sort | uniq >unver.txt
$ cat <<EOD >
digraph {
	graph [overlap=false, size=14];
	root="$(go list -m)";
	node [  shape = plaintext, fontname = "Helvetica", fontsize=24];
	"$(go list -m)" [style = filled, fillcolor = "#E94762"];
$ cat unver.txt | awk '{print "\""$1"\" -> \""$2"\""};' >>
$ echo "}" >>
$ sed -i 's+\("[^/]*/\)\([^"]*"\)+\1\\n\2+g'
$ sfdp -Tsvg -o graph.svg

Version details

go version go1.12.5 linux/amd64
You can’t perform that action at this time.