{"record":{"id":"37dad4d09f1239da","repo":"dagger/dagger","slug":"normalize-git-s-w","errorCode":null,"errorMessage":"normalize .git/%s: %w","messagePattern":"normalize \\.git/(.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/git_bundle.go","lineNumber":824,"sourceCode":"\t\treturn err\n\t}\n\tfor i, ref := range refs {\n\t\tdst := ref.Name\n\t\tif !strings.HasPrefix(dst, \"refs/\") {\n\t\t\tdst = \"refs/dagger/bundle/imported/\" + strconv.Itoa(i)\n\t\t}\n\t\tout, err := runGitEnv(ctx, repoDir, \"rev-parse\", \"--verify\", dst+\"^{object}\")\n\t\tif err != nil || strings.TrimSpace(out) != ref.SHA {\n\t\t\treturn fmt.Errorf(\"imported git bundle ref %q does not resolve to %s\", ref.Name, ref.SHA)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc normalizeCanonicalGitDir(gitDir string) error {\n\tfor _, p := range []string{\"logs\", \"hooks\", \"branches\", \"description\", \"FETCH_HEAD\", \"COMMIT_EDITMSG\"} {\n\t\tif err := os.RemoveAll(filepath.Join(gitDir, p)); err != nil {\n\t\t\treturn fmt.Errorf(\"normalize .git/%s: %w\", p, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// runGitEnv runs git in dir under a hermetic environment, returning its\n// standard output. Errors carry the\n// standard error stream, which is where git reports what went wrong.\nfunc runGitEnv(ctx context.Context, dir string, args ...string) (string, error) {\n\tgitArgs := make([]string, 0, len(gitEphemeralConfig)+len(args))\n\tgitArgs = append(gitArgs, gitEphemeralConfig...)\n\tgitArgs = append(gitArgs, args...)\n\n\tcmd := exec.CommandContext(ctx, \"git\", gitArgs...)\n\tcmd.Dir = dir\n\tcmd.Env = []string{\n\t\t\"GIT_CONFIG_NOSYSTEM=1\",\n\t\t\"HOME=/dev/null\",","sourceCodeStart":806,"sourceCodeEnd":842,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/core/git_bundle.go#L806-L842","documentation":"normalizeCanonicalGitDir strips non-essential entries (logs, hooks, branches, description, FETCH_HEAD, COMMIT_EDITMSG) from a reconstructed .git to make it canonical. This error wraps an os.RemoveAll failure for one of those entries, meaning the engine could not clean the git dir.","triggerScenarios":"os.RemoveAll fails on a path inside the .git directory: permission-denied on a file/dir, a read-only mount, an immutable file attribute, or a path locked by another process.","commonSituations":"Running the engine against a filesystem with restricted permissions; .git mounted read-only; leftover git locks from a concurrent process; unusual attributes (e.g. chattr +i) on .git contents.","solutions":["Check filesystem permissions on the reconstructed .git directory and ensure the engine process can delete entries","Remove any immutable flags (chattr -i) or read-only mounts from the git dir","Stop processes holding locks on .git (e.g. another git invocation) and clear stale lock files","Retry the operation; if persistent, wipe the cached git dir and re-materialize"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"for _, p := range []string{\"logs\",\"hooks\"} {\n    if _, err := os.Stat(filepath.Join(gitDir, p)); err != nil && !os.IsPermission(err) { continue }\n    if !isWritable(filepath.Join(gitDir, p)) { return fmt.Errorf(\"%s not writable\", p) }\n}","typeGuard":"func isPathWritable(path string) bool {\n    f, err := os.CreateTemp(filepath.Dir(path), \".probe\")\n    if err != nil { return false }\n    f.Close(); os.Remove(f.Name()); return true\n}","tryCatchPattern":"if err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) { /* check permissions on pe.Path */ }\n}","preventionTips":["Run the engine with a user that owns the cache filesystem","Avoid read-only or immutable mounts for git dirs","Clear stale git lock files"],"tags":["git","filesystem","permissions"],"backgroundTag":"git-dir-cleanup-failed","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}