go list answers the question you are actually asking before a build breaks: which package did Go resolve, and which module is it pinned to. This walkthrough builds working commands for three jobs: reading package fields, pulling JSON for a script, and checking module upgrades. The examples match Go go1.26.1 on this machine.
The Debian package database reports golang-go 2:1.22~2build1, but the executable found first in PATH is the Linuxbrew toolchain, a mismatch that has sent more than one person chasing the wrong bug. Always check the binary you are actually invoking.
Allow about fifteen minutes. You need a Go toolchain and a readable module or package tree. These commands inspect source and module metadata: they do not edit go.mod, install packages or build an executable. Network access may be used when module information is not already cached, especially with upgrade queries.
Start by checking the command and version:
$ command -v go
/home/linuxbrew/.linuxbrew/bin/go
$ go version
go version go1.26.1 linux/amd64
The manual page installed with golang-go is generated from Go's go help list, but flags and fields can change between releases. If your version differs, run go help list and check the fields your script needs before copying an example into automation.
Checkpoint: the version and path above describe the program whose output you are about to trust. Do not diagnose a module result using a different Go installation reached through an editor, service account or CI runner.
With no special flags, go list prints one import path per named package. Use package patterns such as fmt, net/http or ./... from a module directory:
$ go list fmt net/http
fmt
net/http
The -f option applies a Go template to each package, which is what you want when a shell script needs a small, stable answer rather than a large JSON document:
$ go list -f '{{.ImportPath}} {{.Name}} {{.Standard}}' fmt net/http
fmt fmt true
net/http http true
Templates use Go's package-template syntax. The manual documents the join helper, so this prints imports on one line:
$ go list -f '{{.ImportPath}}: {{join .Imports ", "}}' net/http
net/http: bufio, bytes, context, crypto/tls, ...
The exact imports depend on the selected Go release and build context. Treat the output shown here as a shape, not as a complete list to compare byte for byte.
Reach for -json when a program needs named fields without parsing human-oriented text:
$ go list -json fmt | sed -n '1,18p'
{
"Dir": "/home/linuxbrew/.linuxbrew/Cellar/go/1.26.1/libexec/src/fmt",
"ImportPath": "fmt",
"Name": "fmt",
"Doc": "Package fmt implements formatted I/O with functions analogous to C's printf and scanf.",
"Root": "/home/linuxbrew/.linuxbrew/Cellar/go/1.26.1/libexec",
"Goroot": true,
"Standard": true,
...
}
In a real parser, consume JSON rather than piping through sed. You can request only the fields you need after -json, separated by commas, for example go list -json=ImportPath,Name,Module ./.... Named fields are guaranteed to appear, and skipping the rest can reduce the work Go performs.
Tip: the default file lists are relative to .Dir, but paths in .Dir, .Root and .Export are absolute. That mismatch is a common source of broken reports: join a relative file name to .Dir before opening it, and do not assume every path in the JSON shares the same base.
Add -deps when you need the named packages plus their complete dependency closure:
$ go list -deps -f '{{.ImportPath}} {{.DepOnly}}' net/http | tail -n 3
net/http true
net/http/httptrace true
net/http/httputil false
The order is depth-first post-order: dependencies appear before the package that needs them. The exact tail varies with the toolchain and package graph. .DepOnly is true for packages reached only as dependencies and false for packages named on the command line, which is useful for inventory work, but the output can be large.
Use -find when you only need to identify packages and do not want dependency resolution: it leaves Imports and Deps empty. The manual says -find cannot be combined with -deps, -test or -export, so do not add those flags to a discovery command out of habit.
Add -m to switch from packages to modules. From a module directory, go list -m shows the main module; go list -m all shows it followed by active dependencies:
$ cd /path/to/your/module
$ go list -m all
This prints the main module followed by its active dependencies, one per line. The paths and versions come from your go.mod and module cache, so there is no universal output to copy into a test. A replacement is shown with =>; it may point to a local directory or another module version. The replacement's directory is the source Go actually uses, so do not treat the left-hand module as the directory being compiled.
For a machine-readable report, combine -m and -json. To ask for upgrade information, add -u:
$ go list -m -u -json all > /tmp/go-modules.json
$ test -s /tmp/go-modules.json && echo 'module report written'
module report written
This may contact a module proxy to discover newer versions. It does not update go.mod by itself. A newer version shows up in the module's Update field, and a retracted current version is reported when the relevant metadata is available. Review upgrade and retraction information before changing dependencies.
Safety warning: do not feed upgrade output directly to a dependency update command. go list -m -u is an inspection step. If you later edit module requirements, preserve the repository's normal review and rollback process. The temporary report can be removed with rm -- /tmp/go-modules.json after review; that deletion is not needed for the Go project itself.
By default, an erroneous package is reported on standard error and quietly omitted from normal output. Use -e when a diagnostic tool must receive a record for the bad package instead:
$ go list -e -json ./...
{
"ImportPath": "example.invalid/my-service/broken",
"Incomplete": true,
"Error": {
"Err": "..."
}
}
Do not treat a successful process exit, or a non-empty JSON stream, as proof that every package loaded cleanly. Check Incomplete, Error and DepsErrors when errors matter. Without -e, a missing package can vanish from the result while its error sits alone on standard error.
A compact shell check can preserve the exit status:
if go list -json ./... > packages.json; then
printf '%s\n' 'package report written'
else
status=$?
printf 'go list failed with status %s\n' "$status" >&2
rm -f -- packages.json
exit "$status"
fi
Here packages.json is ordinary working data, not Go configuration. Choose a new destination or remove it before rerunning if stale output would mislead a later step. No elevated privilege is normally required; if the source tree is unreadable, fix its ownership or permissions through your normal administration process rather than running the whole analysis as root.
go binary and version used by the command.Incomplete and status codes instead of quietly reusing bad output.