#1153300 dh-go: 1.70 writes a replace next to the use for the same module, FTBFS

#1153300#5
Date:
2026-11-07 00:32:36 UTC
From:
To:
1.70 cannot build a multi-module package when that package's own -dev is also
installed under /usr/share/gocode/src. That is the case in autopkgtest for
every package, and in a plain build for a package whose build dependencies
pull its own -dev back in. otel is in the second group: it build depends on
golang-google-grpc-dev, which depends on golang-opentelemetry-otel-dev, and
dh-go says so during configure, "go.opentelemetry.io/otel is already
installed. Please check for circular dependencies."

golang-opentelemetry-otel 1.46.0-3, unchanged source that is in testing, in a
clean sid chroot with the 1.70 from the mirror:

  go: workspace module go.opentelemetry.io/otel/sdk/metric is replaced at all
      versions in the go.work file. To fix, remove the replacement from the
      go.work file or specify the version at which to replace the module.
  dh_auto_build: error: cd _build && go install -trimpath -v -p 24 returned
      exit code 1

44 errors, 22 distinct modules, every one of them in the package's own
DH_GOLANG_BUILDPKG. This is a plain build, not autopkgtest.

The same log carries 20460 copies of

  Use of uninitialized value $_ in string eq at
    /usr/share/perl5/Debian/Debhelper/Buildsystem/golang.pm line 1051

Line 1051 is the skip that 1.70 changed:

  -    next if grep { $_ eq $module } @targets;
  +    next if grep { $_ eq $module } @use_modules;

@use_modules is all undef by then. It is wiped twelve lines above, at line 1000:

  @deps_to_check = map { $this->_get_go_module_dependencies($_) } @use_modules;

In map, $_ is an alias to the array element. _get_go_module_dependencies reads
go.mod with an unlocalised "while (<$gomod>)", which assigns to the global $_,
so every call overwrites the element it was passed and leaves undef there at
EOF. The grep then matches nothing, each module in the workspace also gets a
replace, and go refuses the workspace.

Reproducer without dh-go:

  sub reads_a_file {
      open(my $fh, "<", "/etc/hostname") or die $!;
      while (<$fh>) { }
      return ("dep");
  }
  my @use_modules = ("mod/a", "mod/b", "mod/c");
  my @deps = map { reads_a_file($_) } @use_modules;
  # @use_modules is now (undef, undef, undef)
  # grep { $_ eq "mod/a" } @use_modules finds nothing

1.69 was not affected because @targets never passes through that map. The
change at 1051 is correct in itself, it just landed on top of this.

Fix, in _get_go_module_dependencies, before the loop that reads go.mod, also
sent as https://salsa.debian.org/go-team/dh-go/-/merge_requests/10 :

#1153300#10
Date:
2026-11-07 01:46:05 UTC
From:
To:
My first paragraph made this sound like a multi-module problem. It is not, and
the scope is wider than I wrote.

A third package reproduces it: golang-github-knqyf263-nested 0.0.1-4, a single
module at the root with no /vN and no submodules. Under
autopkgtest-virt-unshare on sid, with the 1.70 from the archive, the test fails
with one "Use of uninitialized value $_ ... line 1051" and two "workspace
module github.com/knqyf263/nested is replaced at all versions", and with 1.70
plus the patch it passes with neither. Also on salsa:
https://salsa.debian.org/go-team/packages/golang-github-knqyf263-nested/-/jobs/10608315

So the condition is only this: the module being built is also installed under
/usr/share/gocode/src. The multi-module layout changes how many errors you
get, 44 for otel against 2 for nested, not whether you get them. That means
every dh-go package is affected under autopkgtest, where its own -dev is
installed by definition, which matches the roughly 50 reverse dependency
autopkgtests in the migration excuses rather than only the multi-module ones.

That run is not mine. I checked the two logs on the same machine and they say
FAIL and PASS as described.