{"record":{"id":"50fd5490255cb566","repo":"golang/go","slug":"copying-vet-tool-stdout-w","errorCode":null,"errorMessage":"copying vet tool stdout: %w","messagePattern":"copying vet tool stdout: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/work/exec.go","lineNumber":1780,"sourceCode":"\t\tdefer f.Close() // ignore error\n\t\tif err := VetHandleStdout(f); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tf.Seek(0, io.SeekStart)           // ignore error\n\t\tcache.Default().Put(stdoutKey, f) // ignore error\n\t}\n\n\treturn nil\n}\n\nvar stdoutMu sync.Mutex // serializes concurrent writes (of e.g. JSON values) to stdout\n\n// copyToStdout copies the stream to stdout while holding the lock.\nfunc copyToStdout(r io.Reader) error {\n\tstdoutMu.Lock()\n\tdefer stdoutMu.Unlock()\n\tif _, err := io.Copy(os.Stdout, r); err != nil {\n\t\treturn fmt.Errorf(\"copying vet tool stdout: %w\", err)\n\t}\n\treturn nil\n}\n\n// linkActionID computes the action ID for a link action.\nfunc (b *Builder) linkActionID(a *Action) cache.ActionID {\n\tp := a.Package\n\th := cache.NewHash(\"link \" + p.ImportPath)\n\n\t// Toolchain-independent configuration.\n\tfmt.Fprintf(h, \"link\\n\")\n\t// Hash the resolved buildmode (ldBuildmode), not cfg.BuildBuildmode,\n\t// so that -buildmode=default produces the same build ID as the\n\t// buildmode it resolves to. See go.dev/issue/63559.\n\tfmt.Fprintf(h, \"buildmode %s goos %s goarch %s\\n\", ldBuildmode, cfg.Goos, cfg.Goarch)\n\tfmt.Fprintf(h, \"import %q\\n\", p.ImportPath)\n\tfmt.Fprintf(h, \"omitdebug %v standard %v local %v prefix %q\\n\", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix)\n\tfmt.Fprintf(h, \"defaultgodebug %q\\n\", p.DefaultGODEBUG)","sourceCodeStart":1762,"sourceCodeEnd":1798,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/work/exec.go#L1762-L1798","documentation":"copyToStdout copies a reader (typically the vet tool's stdout) to os.Stdout while holding stdoutMu, which serializes concurrent JSON writes to stdout. If io.Copy fails - usually a broken pipe or I/O error - this wraps the cause. The lock prevents interleaved output from parallel vet actions.","triggerScenarios":"Fires in copyToStdout when io.Copy(os.Stdout, r) returns an error, while streaming vet/build tool output to the terminal or a pipe.","commonSituations":"Piping `go vet` to a consumer that exits early (head, less), closing the terminal mid-build, redirecting to a full disk, or any downstream reader closing its stdin.","solutions":["If piping to head/less the error is expected and harmless - ignore it","If redirecting to a file, confirm the destination is writable and has space","Run go vet without a pipe to see if the error persists"],"exampleFix":"// before (broken pipe from head)\ngo vet ./... | head -5\n// after\ngo vet ./... 2>&1 | head -5 || true","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// When piping go output, tolerate SIGPIPE / broken pipe\nout, err := cmd.CombinedOutput()\nif err != nil && !errors.Is(err, syscall.EPIPE) {\n    return err\n}","preventionTips":["Avoid piping go vet/build into tools that close stdin early (head without -q)","Redirect to a file when capturing large outputs","In CI, set stdout to a file or /dev/null rather than a pipe that may close"],"tags":["go-toolchain","vet","io","broken-pipe"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}