go: cmd/go: "unexpected module path" error lacks enough detail to find underlying problem (docker Sirupsen/logrus mismatch)

What version of Go are you using (go version)?

Go 1.11.1

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

Linux, amd64

Problem

Our go.mod has the following dependency, along with many other dependencies:

github.com/sirupsen/logrus v1.1.1

When running go mod vendor, I get the following error:

go: github.com/Sirupsen/logrus@v1.1.1: parsing go.mod: unexpected module path "github.com/sirupsen/logrus"

I suspect that some deeper indirect dependency is also using logrus, but with the incorrect (old) capital S. However, the error message isn’t giving me enough detail to track it down. It would be nice if the tool gave me a list of the modules or packages that are using the conflicting import path.

Example

https://github.com/jayschwa/go28489

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 64
  • Comments: 36 (20 by maintainers)

Commits related to this issue

Most upvoted comments

if anyone encounters this issue again, a simple solution is to add the following to your go.mod file: replace github.com/Sirupsen/logrus v1.4.2 => github.com/sirupsen/logrus v1.4.2

Replace the version with whatever you need to use.

Have you tried go mod why ?

@maguro in my case I rename all “Sirupsen” to “sirupsen” from my dependencies

@maguro, a TL;DR workaround to what? The issue reported here is a less-than-helpful error message, but presumably the thing you want to work around is some error itself⸮

Have you tried go mod why?

It produces the same error with no additional info.

github.com/docker/docker does not follow semver so we need to do a separate go get for the latest version of that

I should note that I am using a very recent pseudo-version of docker in my example, and the problem still occurs: github.com/docker/docker v0.7.3-0.20181027010111-b8e87cfdad8d

Ideally, docker should start using go.mod and tag its repo correctly, but in the meantime, it would be nice to get more details from the error message so these sorts of problems are easier to diagnose. Nothing in the error message indicates that the conflict is coming from docker or one of its dependencies.

The underlying cause here is almost certainly the same as https://github.com/golang/go/issues/26208, although per the description, not obviously so. Not least because time (and releases) have passed since that issue was reported.

There are a number of issues/factors at play here:

  • github.com/docker/docker does not follow semver so we need to do a separate go get for the latest version of that
  • github.com/docker/docker@v17.05.0-ce (for example) is not a module
  • github.com/docker/docker@v17.05.0-ce’s dependencies are managed via vendor.conf and are sufficiently esoteric (dependencies not using semver, etc) that we need to go mod init and “merge” those dependencies into our main module’s go.mod
  • github.com/Sirupsen/logrus vs github.com/sirupsen/logrus is a long standing issue. GitHub redirects github.com/Sirupsen/logrus to github.com/sirupsen/logrus
  • As of v1.1.0, github.com/sirupsen/logrus/github.com/Sirupsen/logrus is now a module, github.com/sirupsen/logrus. Hence in module mode:
$ go get github.com/Sirupsen/logrus
go: github.com/Sirupsen/logrus@v1.1.1: parsing go.mod: unexpected module path "github.com/sirupsen/logrus"
  • github.com/Sirupsen/logrus is used as such consistently in github.com/docker/docker@v17.05.0-ce and all its dependencies. This means that any use of github.com/sirupsen/logrus in module mode is a problem (case-insensitive import collision: "github.com/sirupsen/logrus" and "github.com/Sirupsen/logrus") but also that we need to use github.com/Sirupsen/logrus (sic) prior to v1.1.0 when it became the module github.com/sirupsen/logrus

Hence we end up needing to do something like the following (notice we get the pre v1.1.0 version of github.com/Sirupsen/logrus/github.com/sirupsen/logrus via the gomodmerge):

https://gist.github.com/myitcv/f7270ab81ab45aa286f496264f034b56

To my mind, the v1.1.0 module-enabled release of github.com/Sirupsen/logrus/github.com/sirupsen/logrus is a breaking change; because an import path of github.com/Sirupsen/logrus now no longer works when in module mode (the irony). Hence I think the module release of github.com/Sirupsen/logrus/github.com/sirupsen/logrus should in fact have been a v2 release.

So one potential way forward here is for github.com/Sirupsen/logrus/github.com/sirupsen/logrus to create a new v1 release that reverts the conversion to Go modules and instead release github.com/sirupsen/logrus/v2 as a module.

cc @bcmills and @rsc for any additional thoughts here.

And I was just shown another way around this problem by a colleague (dropping this here for other poor souls who got stuck):

  • remove all entries in go.mod ending in // indirect
  • remove $GOROOT/pkg/all (not sure if required, we did it anyways)
  • add replace github.com/Sirupsen/logrus v1.4.2 => github.com/sirupsen/logrus v1.4.2
  • run go get

After this the problems disappeared.

Main issue were apparently old some old/unused deps causing the clash.

CL 166984 should help with build and list commands. It produces output like this (from the test):

go: example.com/badchain/a@v1.0.0
	-> example.com/badchain/b@v1.0.0
	-> example.com/badchain/c@v1.0.0: parsing go.mod: unexpected module path "example.com/badchain/wrong"

That at least tells you which module has the unexpected import.

Unfortunately, this doesn’t produce intelligible output for go get -u because we don’t preserve the relationships between modules when upgrading the build list. More refactoring needed there.

I just hit this issue, but the above solution did not work for me. What did work was the solution in this very helpful comment by @kolyshkin: https://github.com/moby/moby/issues/39302#issuecomment-504146736

For me, that meant the following:

main.go (or whatever you’re using)

package main

import (
	"github.com/docker/docker/api/types"
	"github.com/docker/engine/client"
	log "github.com/sirupsen/logrus"
)
...

go.mod

module <your-module>

go 1.12

require (
	...
	// This was auto-populated by running `go build`
	github.com/docker/engine v0.0.0-20190822205725-ed20165a37b4
	// Also auto-populated
	github.com/sirupsen/logrus v1.4.2
	...
)

// Put this in by hand, whatever version you want to use
replace github.com/docker/docker => github.com/docker/engine v19.03.0
// rewritten as follows after running `go build`
// replace github.com/docker/docker => github.com/docker/engine v0.0.0-20190717161051-705d9623b7c1

cc @leelynne and @lynncyrin

@bcmills

If we fetch a module with some casing of its path, and the module declaration indicates a different casing, that’s not a defect in the module — it’s a defect in whatever package is trying to import from that module, and should be diagnosed within that package.

I think a better error would go a long way.

I think this is the same issue with docker/libcompose, where go mod tidy and go mod why were both erroring out prior to saying where the conflict came from. In other words, this error (from using docker/libcompose) doesn’t tell you the real culprit:

go: github.com/Sirupsen/logrus@v1.2.0: parsing go.mod: unexpected module path "github.com/sirupsen/logrus"

It seems like there is already a reproduction here, but here is a simple one showing go mod tidy fail for a libcompose client:

mkdir -p /tmp/scratchpad/docker-simple-libcompose-client
cd /tmp/scratchpad/docker-simple-libcompose-client

go1.12beta1 mod init github.com/me/client

cat <<EOF > main.go

package main

import (
        "golang.org/x/net/context"

        "github.com/docker/libcompose/docker"
        "github.com/docker/libcompose/docker/ctx"
        "github.com/docker/libcompose/project"
        "github.com/docker/libcompose/project/options"
)

func main() {
        project, err := docker.NewProject(&ctx.Context{Context: project.Context{ComposeFiles: []string{"docker-compose.yml"},
                        ProjectName:  "my-compose",}, }, nil)

        project.Up(context.Background(), options.Up{})
}
EOF

# run our `go mod tidy` test, which should fail, but error is not as helpful as it could be.

$ go1.12beta1 mod tidy
go: finding github.com/docker/libcompose/project/options latest
go: finding github.com/docker/libcompose/docker/ctx latest
go: finding github.com/docker/libcompose/docker latest
go: finding github.com/docker/libcompose/project latest
go: finding golang.org/x/net/context latest
go: finding github.com/docker/libcompose v0.4.0
go: finding golang.org/x/net latest
go: downloading golang.org/x/net v0.0.0-20181220203305-927f97764cc3
go: downloading github.com/docker/libcompose v0.4.0
go: extracting golang.org/x/net v0.0.0-20181220203305-927f97764cc3
go: extracting github.com/docker/libcompose v0.4.0
go: finding github.com/docker/go-connections/nat latest
go: finding gopkg.in/yaml.v2 v2.2.2
go: finding github.com/docker/docker/api/types latest
go: finding github.com/docker/docker/pkg/jsonmessage latest
go: finding github.com/docker/docker/client latest
go: finding github.com/docker/docker/registry latest
go: finding github.com/docker/docker/reference latest
go: finding github.com/docker/docker/cliconfig/configfile latest
go: finding github.com/docker/docker/pkg/streamformatter latest
go: finding github.com/stretchr/testify/assert latest
go: finding github.com/docker/go-connections v0.4.0
go: downloading gopkg.in/yaml.v2 v2.2.2
go: downloading github.com/docker/go-connections v0.4.0
go: extracting gopkg.in/yaml.v2 v2.2.2
go: finding github.com/docker/docker/cliconfig latest
go: finding github.com/stretchr/testify v1.2.2
go: extracting github.com/docker/go-connections v0.4.0
go: downloading github.com/stretchr/testify v1.2.2
go: finding github.com/docker/docker/pkg/stdcopy latest
go: extracting github.com/stretchr/testify v1.2.2
go: finding github.com/docker/docker/builder/dockerignore latest
go: finding github.com/docker/docker/pkg latest
go: finding github.com/docker/docker v1.13.1
go: finding github.com/docker/docker/api latest
go: finding github.com/docker/docker/builder latest
go: downloading github.com/docker/docker v1.13.1
go: extracting github.com/docker/docker v1.13.1
go: finding github.com/docker/go-connections/tlsconfig latest
go: finding github.com/docker/docker/api/types/filters latest
go: finding github.com/docker/docker/pkg/archive latest
go: finding github.com/docker/docker/pkg/term latest
go: finding github.com/docker/docker/api/types/strslice latest
go: finding github.com/docker/go-connections/sockets latest
go: finding github.com/docker/docker/pkg/fileutils latest
go: finding github.com/docker/docker/pkg/promise latest
go: finding github.com/docker/docker/runconfig/opts latest
go: finding github.com/docker/docker/pkg/homedir latest
go: finding github.com/docker/docker/api/types/container latest
go: finding github.com/xeipuuv/gojsonschema latest
go: finding github.com/flynn/go-shlex latest
go: finding github.com/docker/docker/runconfig latest
go: finding github.com/docker/docker/pkg/urlutil latest
go: finding github.com/docker/docker/pkg/progress latest
go: finding github.com/docker/go-units v0.3.3
go: downloading github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
go: downloading github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56
go: finding github.com/docker/docker/api/types/network latest
go: extracting github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
go: extracting github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56
go: downloading github.com/docker/go-units v0.3.3
go: finding github.com/Sirupsen/logrus v1.2.0
go: finding github.com/docker/docker/api/types/events latest
go: finding github.com/docker/docker/api/types/registry latest
go: extracting github.com/docker/go-units v0.3.3
go: downloading github.com/Sirupsen/logrus v1.2.0
go: extracting github.com/Sirupsen/logrus v1.2.0
go: github.com/Sirupsen/logrus@v1.2.0: parsing go.mod: unexpected module path "github.com/sirupsen/logrus"
go: error loading module requirements

CC @vito-c

@Liamsi There has been a change in Go tip to make the output better, see https://github.com/golang/go/issues/28489#issuecomment-473297974 above. You can try Go tip via go get golang.org/dl/gotip. (h/t to @FiloSottile for making it!)

@jayschwa ta; adjusted commit to align with that.

In https://github.com/golang/go/issues/28489#issuecomment-434616301 above, @myitcv has a very nice explanation of the somewhat common issue one can hit with docker with a uppercase/lowercase mismatch between github.com/Sirupsen/logrus vs. github.com/sirupsen/logrus, and he also has a worked example there.

The starting point for that worked example above happens to be a docker version from ~18 months ago (in part because of the way docker has been tagging their releases and moving repos around, as touched upon above), and the worked example above ends up with github.com/Sirupsen/logrus (the uppercase version) because that worked example shows finding a consistent set of older versions.

Here is a worked example using a more recent docker version, and ends up with github.com/sirupsen/logrus (the lowercase version), using a simple docker/libcompose client that initially fails with:

go: github.com/Sirupsen/logrus@v1.2.0: parsing go.mod: 
unexpected module path "github.com/sirupsen/logrus"

The following FAQ covers the technique in a bit more detail:

“FAQ: I have a problem with a complex dependency that has not opted in to modules. Can I use information from its current dependency manager?”

First, we clone libcompose in order to run go mod init to convert from the vendor.conf from libcompose to a temporary go.mod, and then use those results to populate the require directives for our client. Here is a worked example of doing that (with a 2018 version of libcompose and docker):

Worked Example: Steps and Results
# ----------------------------------------------------------------------------------------------
# 1. Create a simple libcompose client, which initially fails 'go mod tidy' with error:
#        go: github.com/Sirupsen/logrus@v1.2.0: parsing go.mod: 
#        unexpected module path "github.com/sirupsen/logrus"
# For why this is expected to fail, see discussion in: https://github.com/golang/go/issues/28489
# ----------------------------------------------------------------------------------------------

mkdir -p /tmp/scratchpad/docker-simple-libcompose-client
cd /tmp/scratchpad/docker-simple-libcompose-client

go mod init github.com/my/client

cat <<EOF > main.go

package main

import (
        "golang.org/x/net/context"

        "github.com/docker/libcompose/docker"
        "github.com/docker/libcompose/docker/ctx"
        "github.com/docker/libcompose/project"
        "github.com/docker/libcompose/project/options"
)

func main() {
        project, _ := docker.NewProject(&ctx.Context{Context: project.Context{ComposeFiles: []string{"docker-compose.yml"},
                        ProjectName:  "my-compose",}, }, nil)

        project.Up(context.Background(), options.Up{})
}
EOF

# run our `go mod tidy` test, which we expect to fail (due to Sirupsen/logrus vs. sirupsen/logrus mismatch)
go mod tidy

# Outputs:
# go: finding golang.org/x/net/context latest
# go: finding github.com/docker/libcompose/docker/ctx latest
# ...
# go: downloading github.com/docker/go-units v0.3.3
# go: github.com/Sirupsen/logrus@v1.2.0: parsing go.mod: unexpected module path "github.com/sirupsen/logrus"
# go: error loading module requirements


# -------------------------------------------------------------------------------------------
# 2. Clone libcompose in order to run `go mod init` to convert from `vendor.conf` 
#     to a temporary `go.mod`.
# For why this is a useful technique, see FAQ https://github.com/golang/go/wiki/Modules#i-have-a-problem-with-a-complex-dependency-that-has-not-opted-in-to-modules-can-i-use-information-from-its-current-dependency-manager
# -------------------------------------------------------------------------------------------


git clone https://github.com/docker/libcompose.git /tmp/scratchpad/libcompose-clone
cd /tmp/scratchpad/libcompose-clone

# Latest commit is 213509a from Oct 19 2018 (as of Dec 24 2018)
git checkout -q 213509a

# Initialize a temporary 'go.mod' file here, which will convert the version information from libcompose's vendor.conf
go mod init

# outputs:
# go: creating new go.mod: module github.com/docker/libcompose
# go: copying requirements from vendor.conf

cat go.mod

# -------------------------------------------------------------------------------------------
# 3. Switch back to our libcompose client, and specify the same version of libcompose as we just cloned.
# -------------------------------------------------------------------------------------------

cd /tmp/scratchpad/docker-simple-libcompose-client/
go get github.com/docker/libcompose@213509a 

# -------------------------------------------------------------------------------------------
# 4. Move the requires from the temporary go.mod in the libcompose clone 
#     to our "real" go.mod in our actual libcompose client (deleting any duplicates first from our client go.mod).  
#
#     We could do this manually, but here we will do it by just appending the full set of requires (using 'tail' to skip the 'module' line), 
#     and then let 'go mod tidy' and friends deal with formatting.

tail -n +2 /tmp/scratchpad/libcompose-clone/go.mod >> /tmp/scratchpad/docker-simple-libcompose-client/go.mod

# -------------------------------------------------------------------------------------------
# 5. 'go mod tidy' and 'go build' now succeed for our simple libcompose client.
# -------------------------------------------------------------------------------------------

cd /tmp/scratchpad/docker-simple-libcompose-client/
go mod tidy
go build

# Finally, show the resulting working 'go.mod' file. The first require is from our 'go get', and 
# the remaining require versions were the ones we converted from .../libcompose-clone/vender.conf via 'go mod init'
# and then updated via 'go mod tidy'.

$ cat /tmp/scratchpad/docker-simple-libcompose-client/go.mod

module github.com/my/client

require github.com/docker/libcompose v0.4.1-0.20181019154650-213509acef0f

require (
        github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
        github.com/Microsoft/go-winio v0.3.8 // indirect
        github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
        github.com/containerd/continuity v0.0.0-20170913164642-35d55c5e8dd2 // indirect
        github.com/davecgh/go-spew v0.0.0-20160907170601-6d212800a42e // indirect
        github.com/docker/cli v0.0.0-20171020201719-3352c0e137e8 // indirect
        github.com/docker/distribution v0.0.0-20170726174610-edc3ab29cdff // indirect
        github.com/docker/docker v0.0.0-20180221164450-0ede01237c9a // indirect
        github.com/docker/docker-credential-helpers v0.0.0-20170816090621-3c90bd29a46b // indirect
        github.com/docker/go-connections v0.3.0 // indirect
        github.com/docker/go-units v0.0.0-20170127094116-9e638d38cf69 // indirect
        github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4 // indirect
        github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
        github.com/gogo/protobuf v0.0.0-20170307180453-100ba4e88506 // indirect
        github.com/google/go-cmp v0.2.0 // indirect
        github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f // indirect
        github.com/gorilla/mux v0.0.0-20160317213430-0eeaf8392f5b // indirect
        github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
        github.com/onsi/ginkgo v1.7.0 // indirect
        github.com/onsi/gomega v1.4.3 // indirect
        github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420 // indirect
        github.com/opencontainers/image-spec v0.0.0-20170515205857-f03dbe35d449 // indirect
        github.com/opencontainers/runc v0.0.0-20161109192122-51371867a01c // indirect
        github.com/opencontainers/selinux v1.0.0 // indirect
        github.com/pkg/errors v0.0.0-20161002052512-839d9e913e06 // indirect
        github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0 // indirect
        github.com/sirupsen/logrus v1.0.3 // indirect
        github.com/stretchr/testify v0.0.0-20151202184152-a1f97990ddc1 // indirect
        github.com/xeipuuv/gojsonpointer v0.0.0-20151027082146-e0fe6f683076 // indirect
        github.com/xeipuuv/gojsonreference v0.0.0-20150808065054-e02fc20de94c // indirect
        github.com/xeipuuv/gojsonschema v0.0.0-20160323030313-93e72a773fad // indirect
        golang.org/x/crypto v0.0.0-20160408163010-3fbbcd23f1cb // indirect
        golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
        golang.org/x/time v0.0.0-20160202183820-a4bde1265759 // indirect
        gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
        gopkg.in/check.v1 v1.0.0-20150729080431-11d3bc7aa68e // indirect
        gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
        gotest.tools v2.2.0+incompatible // indirect
)

Awesome, will check that out! Thank you! Update: That change actually resolved the issue I had. Also, thanks for the reminder for gotip.

So one potential way forward here is for github.com/Sirupsen/logrus/github.com/sirupsen/logrus to create a new v1 release that reverts the conversion to Go modules and instead release github.com/sirupsen/logrus/v2 as a module.

I think it’s actually fine to keep the change in v1. Code that uses the “wrong” import path for v1 in is already broken in general anyway: if you try to import the same module with two different cases, you’ll get a case-sensitive import collision (https://github.com/golang/go/issues/26208#issuecomment-411955266), so if we allow modules that depend on each path individually build successfully, they still can’t be combined: they aren’t really “modular”.