Skip to content

cmd/link: "The command line is too long" on Windows when the external linker is a .bat or .cmd wrapper #80862

Open

Description

@hajimehoshi

Go version

go version go1.26.5 windows/amd64 (also seen with go1.26.0-rc.1; go1.25.7 happens to stay under the limit, see below)

Output of go env in your module/workspace:

# Host is windows/amd64 (GitHub Actions windows-latest runner). The failing
# invocation cross-compiles to android/arm64, with the toolchain set by
# gomobile:
GOOS=android
GOARCH=arm64
CGO_ENABLED=1
CC=C:\Android\android-sdk\ndk\27.3.13750724\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android23-clang
CXX=C:\Android\android-sdk\ndk\27.3.13750724\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android23-clang++
GOTMPDIR=
TMP=C:\Users\RUNNER~1\AppData\Local\Temp

# The rest is the runner default; I don't have interactive access to that
# machine to paste a complete `go env` dump.

What did you do?

Real-world case: gomobile bind -target android on a Windows host. gomobile sets CC/CXX to the extension-less NDK path, and on Windows only the NDK's .cmd wrapper is executable, so os/exec resolves ...-clang++ to ...-clang++.cmd (lookExtensions → PATHEXT). Windows runs a .cmd through cmd.exe, whose command-line limit is 8191 characters. The external link command line for that build is about 8.2 KB, so the link fails.

Minimal, toolchain-independent reproducer (Windows, any C compiler on PATH):

cc.bat:

@echo off
gcc %*

hello.go:

package main

func main() {}
$env:CGO_ENABLED = "1"
$env:CC = "$PWD\cc.bat"
# ~10 KB of harmless linker flags: enough to pass cmd.exe's 8191-character
# limit, but well under cmd/link's 30 KB response-file threshold.
$pad = (1..2500 | ForEach-Object { "-L." }) -join " "
go build -ldflags="-linkmode=external -extldflags '$pad'" hello.go

The same build with $env:CC = "gcc" (no .bat wrapper) succeeds, because then CreateProcess is used directly and the limit is 32767 rather than 8191.

What did you see happen?

# command-line-arguments
C:\hostedtoolcache\windows\go\1.26.5\x64\pkg\tool\windows_amd64\link.exe: running C:\Android\android-sdk\ndk\27.3.13750724\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android23-clang++ failed: exit status 1
C:\Android\android-sdk\ndk\27.3.13750724\...\aarch64-linux-android23-clang++ -Wl,-z,relro -shared ... ..\..\go-link-3327393765\000000.o ... -static-libstdc++
The command line is too long.

The command line is too long. is cmd.exe's message, emitted because the external linker resolved to a .cmd.

Full log of a failing run: https://github.com/hajimehoshi/ebiten/actions/runs/31667490080/job/94345055848

Note that the command line looks like only ~4.5 KB in that log. That is cmd/go shortening absolute paths for display ((*Shell).reportCmd → base.ShortPath). The real object paths are C:\Users\RUNNER~1\AppData\Local\Temp\go-link-NNN\NNNNNN.o (64 chars each), printed as ..\..\go-link-NNN\NNNNNN.o (33 chars). With 118 objects that is ~3.7 KB more than displayed, putting the actual command line at ~8.2 KB, i.e. right at cmd.exe's limit.

cmd/link already handles long command lines in passLongArgsInResponseFile, but its threshold is sys.ExecArgLengthLimit = 30 << 10, whose comment derives it from the CreateProcess limit of 32 KB. That is ~4x too high when the external linker is a batch file, so the response-file path is never taken here.

I confirmed the threshold behavior on darwin/arm64 (where there is no such low limit, so it does not fail): with the padded -extldflags above, cmd/link hands the external linker a 9015-character argv directly, with no response file.

Why this surfaced with Go 1.26 specifically: 1.26 adds runtime/cgo/gcc_clearenv.c, taking runtime/cgo from 11 to 12 C files, i.e. one more host object (~64 characters) on the link line. Measured on an android/arm64 -buildmode=c-shared build: 39 objects with go1.25.7, 40 with go1.26.5, with otherwise identical link arguments. That is enough to tip a build that was sitting a few dozen bytes under 8191. So builds of this shape worked with 1.25 and fail with 1.26, though the underlying threshold mismatch is older.

What did you expect to see?

A successful link.

When the resolved external linker on Windows is a .bat or .cmd, the effective command-line limit is cmd.exe's 8191, not CreateProcess's 32767, so passLongArgsInResponseFile should use a correspondingly lower threshold in that case. The fallback machinery already exists and is guarded by a support probe (linkerFlagSupported(..., "@"+response)), and both clang and gcc accept @file, so this looks like a matter of choosing the limit based on whether the linker is a batch file.

This is not specific to one project: the object count on the link line comes from the fixed set of cgo packages rather than from the size of the user's Go code, so essentially every Android build from a Windows host lands at the same ~8.2 KB. Typical user temp directories are also longer than the CI runner's C:\Users\RUNNER~1\... (which is short only because of the 8.3 name), making the overflow worse off-CI.


Filed by Claude (Claude Code) on behalf of @hajimehoshi. The synthetic reproducer above was constructed on macOS and its Go-side behavior (no response file below 30 KB) verified there; the Windows-specific failure is evidenced by the linked CI run rather than by a local Windows execution.

Activity

  1. gopherbot commented on Aug 13, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/814500 mentions this issue: cmd/gomobile: invoke the NDK Clang directly

  2. seankhliao commented on Aug 13, 2026

    @seankhliao
    Member

    cc @golang/windows

  3. added this to the Unreleased milestone on Aug 18, 2026
  4. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Aug 18, 2026
  5. gopherbot commented on Aug 20, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/818741 mentions this issue: cmd/link: use response files earlier for Windows batch linkers

  6. added
    cgoIssues affecting runtime/cgo and cmd/go
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.OS-WindowscgoIssues affecting runtime/cgo and cmd/gocompiler/runtimeIssues related to the Go compiler and/or runtime.help wanted

    Type

    No type

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions