{"record":{"id":"d97cdbd7aea6b55e","repo":"tailscale/tailscale","slug":"running-v-w-out-s-err-s","errorCode":null,"errorMessage":"running %v: %w, out=%s, err=%s","messagePattern":"running (.+?): %w, out=(.+?), err=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"version/mkversion/mkversion.go","lineNumber":502,"sourceCode":"type dirRunner string\n\nfunc (r dirRunner) output(prog string, args ...string) (string, error) {\n\tcmd := exec.Command(prog, args...)\n\t// Sometimes, our binaries end up running in a world where\n\t// GO111MODULE=off, because x/tools/go/packages disables Go\n\t// modules on occasion and then runs other Go code. This breaks\n\t// executing \"go mod edit\", which requires that Go modules be\n\t// enabled.\n\t//\n\t// Since nothing we do here ever wants Go modules to be turned\n\t// off, force it on here so that we can read module data\n\t// regardless of the environment.\n\tcmd.Env = append(os.Environ(), \"GO111MODULE=on\")\n\tcmd.Dir = string(r)\n\tout, err := cmd.Output()\n\tif err != nil {\n\t\tif ee, ok := err.(*exec.ExitError); ok {\n\t\t\treturn \"\", fmt.Errorf(\"running %v: %w, out=%s, err=%s\", cmd.Args, err, out, ee.Stderr)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"running %v: %w, %s\", cmd.Args, err, out)\n\t}\n\treturn strings.TrimSpace(string(out)), nil\n}\n\nfunc (r dirRunner) ok(prog string, args ...string) bool {\n\tcmd := exec.Command(prog, args...)\n\tcmd.Dir = string(r)\n\treturn cmd.Run() == nil\n}\n","sourceCodeStart":484,"sourceCodeEnd":514,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/version/mkversion/mkversion.go#L484-L514","documentation":"This is dirRunner.output's diagnostic wrapper for a child command that ran and exited non-zero: it prints the argv, wrapped error, captured stdout (out=) and the child's stderr (err=). Every git failure inside mkversion (finding git root, getting hash/date, cache rev-parse, etc.) surfaces through this wrapper and is then re-wrapped by the caller - so this message is where the real git diagnostics live.","triggerScenarios":"Any git subcommand failing: rev-parse in a non-repo, rev-parse HEAD with unborn branch, cat-file/rev-list on missing refs in the cache, fetch while offline. Recognizable by '*exec.ExitError' semantics and the err= field carrying git's stderr.","commonSituations":"Triaging any mkversion build failure; the out=/err= fields tell you whether it is repo state, refs, or network.","solutions":["Copy the argv shown in the message and run it manually in the same directory (note commands against the cache run with cwd = cache dir)","Read the err=<stderr> field - it contains git's actual fatal message","Fix the underlying condition per that message (commit, ref, network, permissions)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isSubprocessExitError(err error) bool {\n\tvar ee *exec.ExitError\n\tfor e := err; e != nil; e = errors.Unwrap(e) {\n\t\tif errors.As(e, &ee) {\n\t\t\treturn true\n\t\t}\n\t\tif strings.Contains(e.Error(), \"running [\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","tryCatchPattern":"v, err := mkversion.InfoFrom(dir)\nif err != nil {\n\t// The message embeds argv + out= + err=<git stderr>.\n\t// Extract and surface the stderr field for actionable diagnostics.\n\tif i := strings.Index(err.Error(), \"err=\"); i != -1 {\n\t\treturn fmt.Errorf(\"git failed: %s\", err.Error()[i:])\n\t}\n\treturn err\n}","preventionTips":["When triaging, re-run the exact argv from the message in the same cwd","Treat err= (stderr) as the source of truth for the root cause"],"tags":["subprocess","git","diagnostics","build-tooling"],"backgroundTag":"subprocess-nonzero-exit","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}