{"record":{"id":"40390e8825c65215","repo":"tailscale/tailscale","slug":"finding-git-root-w","errorCode":null,"errorMessage":"finding git root: %w","messagePattern":"finding git root: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"version/mkversion/mkversion.go","lineNumber":122,"sourceCode":"}\n\n// Info constructs a VersionInfo from the current working directory and returns\n// it, or terminates the process via log.Fatal.\nfunc Info() VersionInfo {\n\tv, err := InfoFrom(\"\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn v\n}\n\n// InfoFrom constructs a VersionInfo from dir and returns it, or an error.\nfunc InfoFrom(dir string) (VersionInfo, error) {\n\trunner := dirRunner(dir)\n\n\tgitRoot, err := runner.output(\"git\", \"rev-parse\", \"--show-toplevel\")\n\tif err != nil {\n\t\treturn VersionInfo{}, fmt.Errorf(\"finding git root: %w\", err)\n\t}\n\trunner = dirRunner(gitRoot)\n\n\tmodBs, err := os.ReadFile(filepath.Join(gitRoot, \"go.mod\"))\n\tif err != nil {\n\t\treturn VersionInfo{}, fmt.Errorf(\"reading go.mod: %w\", err)\n\t}\n\tmodPath := modfile.ModulePath(modBs)\n\n\tif modPath == \"\" {\n\t\treturn VersionInfo{}, fmt.Errorf(\"no module path in go.mod\")\n\t}\n\tif modPath == \"tailscale.com\" {\n\t\t// Invoked in the tailscale.com repo directly, just no further info to\n\t\t// collect.\n\t\tv, err := infoFromDir(gitRoot)\n\t\tif err != nil {\n\t\t\treturn VersionInfo{}, err","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/version/mkversion/mkversion.go#L104-L140","documentation":"mkversion.InfoFrom runs `git rev-parse --show-toplevel` in the directory you pass (empty string = cwd) to locate the git checkout it derives version strings from. This error means that command failed: the directory is not inside a git work tree, the .git directory is corrupt, or git could not run at all. The wrapped error (usually a dirRunner 'running [git rev-parse --show-toplevel]...' message) carries git's own 'fatal: not a git repository' diagnostics.","triggerScenarios":"Calling mkversion.Info() or InfoFrom(dir) with dir outside any git repository, with a broken/missing .git directory, or in an environment where the git binary is missing from PATH (surfaced via the 2076-style 'running %v' wrapper).","commonSituations":"Running the tailscale build/version-stamping step from an exported source tarball, from the wrong directory in CI, or inside a minimal container that lacks git or has HOME unset so git cannot be found.","solutions":["Run the build from the root of a git clone and verify with `git rev-parse --show-toplevel` that it prints the expected path","If git is not installed (see a 'running [git ...]' wrapper mentioning exec/not-found), install git and ensure it is on PATH","Repair the checkout: confirm `git status` works; if .git is corrupt, re-clone","Pass a directory that is actually inside the repository instead of a temp/output directory"],"exampleFix":"# before: building from an unpacked tarball\nmkversion.InfoFrom(\"/build/tailscale-src\")   # no .git -> finding git root: ...\n\n# after: build from a real clone\n$ git clone https://github.com/tailscale/tailscale /build/tailscale\nmkversion.InfoFrom(\"/build/tailscale\")","handlingStrategy":"validation","validationCode":"// Verify dir is inside a git work tree before calling mkversion.InfoFrom.\nfunc inGitRepo(dir string) bool {\n\tcmd := exec.Command(\"git\", \"-C\", dir, \"rev-parse\", \"--show-toplevel\")\n\treturn cmd.Run() == nil\n}\n\nif !inGitRepo(buildDir) {\n\tlog.Fatalf(\"%s is not a git checkout; run the build from a clone\", buildDir)\n}\nv, err := mkversion.InfoFrom(buildDir)","typeGuard":"func isGitRootError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"finding git root:\")\n}","tryCatchPattern":"v, err := mkversion.InfoFrom(dir)\nif err != nil {\n\tif isGitRootError(err) {\n\t\tlog.Fatalf(\"run the build from a git checkout: %v\", err)\n\t}\n\treturn fmt.Errorf(\"mkversion: %w\", err)\n}","preventionTips":["Always build from a git clone, never from an exported tarball","Install git in CI images and assert `git --version` before the build step","Log the cwd before invoking mkversion so bad build directories are obvious"],"tags":["git","go","build-tooling","mkversion"],"backgroundTag":"not-a-git-repository","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}