Every team I’ve been on has at some point had a merge conflict in go.sum that someone tried to “resolve” by keeping both halves. The result is a broken lockfile, a subtly different dependency graph, and at least one sad afternoon. This post is me writing down the right way to handle it so I can send people a link instead of having the same conversation again.

First, what the files actually are:

  • go.mod describes your module’s direct and some indirect dependencies, with minimum versions.
  • go.sum contains cryptographic hashes of the exact module versions that go.mod resolves to, including all transitive dependencies. It’s a lockfile.

When two branches add or update dependencies independently, both files will often conflict. The correct way to resolve is NOT to manually merge the text. It’s to let the tooling regenerate:

# after resolving other conflicts, from the merge:
git checkout --theirs go.mod go.sum   # or --ours, whichever
go mod tidy
git add go.mod go.sum

go mod tidy rereads your go.mod, walks your source code, and adjusts go.mod and go.sum to contain exactly what’s needed. It’s idempotent and safe to run.

If the two branches added different dependencies, you want BOTH sets. In that case:

# take your branch's version
git checkout --ours go.mod go.sum
# add the deps the other branch added by running tidy on a merge of the code
go mod tidy
# confirm the required versions are what you expect
go list -m all | grep somepackage
git add go.mod go.sum

The trick is that go mod tidy looks at your actual source code. If both branches added imports in code, after the source merge, the imports are present, and tidy figures out the versions.

A subtler case: both branches BUMPED the same dependency. go mod tidy will pick the minimum that satisfies all the require statements. Since both branches bumped, both require lines want a higher version. The merge resolution should pick the higher of the two (or the one you actually want), put it in go.mod, and re-tidy:

# after opening go.mod and picking the desired version for the conflicting line
go mod tidy
go build ./...  # verify
git add go.mod go.sum

DO NOT ever:

  1. Manually edit go.sum. The hashes are cryptographic and you cannot meaningfully edit them.
  2. Accept both halves of a go.sum conflict. You’ll get duplicate entries for a module version, which go mod verify will reject, or worse, you’ll get conflicting hashes for the same version which is an instant-fail.
  3. Delete go.sum and let Go “regenerate it.” It will regenerate, but against whatever versions happen to be in module proxy caches right now, which might not be what your coworker had. Always regenerate via go mod tidy, not by deletion.

Some specific conflict shapes and what to do:

Shape 1: go.sum has 200 conflict markers. This happens when both branches upgraded a major transitive dependency. The fix is to forget the text merge:

git checkout HEAD -- go.sum  # reset go.sum to your branch's version
go mod tidy                   # regenerate it
git add go.sum

Shape 2: go.mod has a conflicting version in a require line. Open go.mod, pick one version (probably the newer), delete the conflict markers, save, then:

go mod tidy
go build ./...

If go build fails, you picked a version that doesn’t have some API the code uses. Fix that, or pick the other version.

Shape 3: replace or exclude directives conflict. These are rare but real. Usually one branch added a replace for a local path (vendoring work in progress), the other branch added something else. Resolve manually in go.mod, then go mod tidy.

A couple of prevention tips that have helped my teams:

  • go mod tidy in CI. Fail the build if go mod tidy would change anything. Put it in a pre-merge check. This prevents the “lockfile was never regenerated after a dependency change” class of bug from reaching main.
go mod tidy
if ! git diff --quiet go.mod go.sum; then
  echo "go.mod or go.sum out of date. Run 'go mod tidy'."
  exit 1
fi
  • Don’t commit IDE-added imports you don’t use. Some IDEs will auto-import packages on autocomplete, which can cause go mod tidy to ADD dependencies you didn’t intend.

  • Use go mod download in your Dockerfile before copying source. This caches dependencies in a Docker layer. Any change to go.mod/go.sum invalidates the layer, but code-only changes don’t.

  • Pinning with replace is fine, but leave a comment. If you’ve got replace example.com/foo => ../foo because you’re iterating on a local fork, leave a // TODO: remove once v1.2.3 released comment so it doesn’t live forever.

Most go.sum conflicts are annoying but quick. The bad cases are when someone resolved a conflict by accepting both halves and pushed to main, because then the whole team gets a broken checkout and it takes a while to figure out why go build is complaining about hash mismatches. Build the CI check. Teach your team to use go mod tidy. The conflicts get boring, and that’s what you want.