Skip to content

cmd/go: spurious leading blank line before ok when a passing test writes unterminated output #79786

Description

@bushnell-deshaw

Go version

go version go1.26.3 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/u/bushnell/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/u/bushnell/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2214924246=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/u/bushnell/go/pkg/mod'
GONOPROXY='*.deshaw.com'
GONOSUMDB='*.deshaw.com'
GOOS='linux'
GOPATH='/u/bushnell/go'
GOPRIVATE='*.deshaw.com'
GOPROXY='https://bushnell:yo80jomsa%23Td9qQlkrryBOiNDoGt98E%26%29s%21rxbzHGrh%40iB%24G2gf2797YGfF%29FMk8@artifactory.deshaw.com/artifactory/api/go/go'
GOROOT='/proj/lang/go/1.26.3/x86_64'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/u/bushnell/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/proj/lang/go/1.26.3/x86_64/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.26.3'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Run a passing test binary which writes output that does not end with a newline (typically a stderr diagnostic that uses %q or otherwise forgets the trailing \n), go test — in non-verbose, non-JSON mode. See this example:

// blank_test.go
package blanklinerepro

import (
    "fmt"
    "os"
    "testing"
)

func TestPass(t *testing.T) {}

func TestMain(m *testing.M) {
    code := m.Run()
    // Last write to stderr doesn't end in '\n'. %q is a common way to land here:
    // it emits a closing '"' as the final byte.
    fmt.Fprintf(os.Stderr, "teardown problem: %q", "some detail")
    os.Exit(code)
}

Tested with go version go1.26.3 linux/amd64. The relevant cmd/go code is unchanged on tip, so this almost certainly reproduces on every Go version since the linked CL landed.

We hit this in the wild when a teardown helper in our test infrastructure was calling something like this (note that despite the output, the tests were correctly passing):

fmt.Fprintf(os.Stderr, "failing output from subprocess: %v:\n%q", err, string(errout))

note format ending in %q, no trailing \n. When we used this helper in TestMain for some packages, every go test ./... run produced a stray blank line in front of those packages' ok lines, baked into the test cache. We fixed our format string locally, but the underlying cmd/go behavior was surprising and seems worth correcting upstream.

What did you see happen?

Running the test emits a blank line in front of the ok pkg T.TTTs summary line, with nothing above it. The captured output itself is correctly suppressed (the test passed), so the blank line is a separator dangling in front of empty space. The result is cached, so the blank line keeps reappearing on every cache hit.

$ go test -count=1 ./...
                       <-- blank line
ok  blanklinerepro  0.004s
$ go test -count=1 ./... | xxd | head -1
00000000: 0a 6f 6b 20 20 09 62 6c 61 6e 6b 6c 69 6e 65 72  .ok  .blankliner
<LF>ok  blanklinerepro  0.004s<LF>

What did you expect to see?

A passing, non-verbose run whose captured output is being suppressed should print:

ok  blanklinerepro  0.004s

with no leading blank line. The separator only makes sense when there is visible output above the ok line that needs to be ended with a newline.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixPendingIssues that have a fix which has not yet been reviewed or submitted.GoCommandcmd/go

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    All-But-Submitted

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions