dagger/dagger · error
normalize .git/%s: %w
Error message
normalize .git/%s: %w
What it means
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.
Source
Thrown at core/git_bundle.go:824
return err
}
for i, ref := range refs {
dst := ref.Name
if !strings.HasPrefix(dst, "refs/") {
dst = "refs/dagger/bundle/imported/" + strconv.Itoa(i)
}
out, err := runGitEnv(ctx, repoDir, "rev-parse", "--verify", dst+"^{object}")
if err != nil || strings.TrimSpace(out) != ref.SHA {
return fmt.Errorf("imported git bundle ref %q does not resolve to %s", ref.Name, ref.SHA)
}
}
return nil
}
func normalizeCanonicalGitDir(gitDir string) error {
for _, p := range []string{"logs", "hooks", "branches", "description", "FETCH_HEAD", "COMMIT_EDITMSG"} {
if err := os.RemoveAll(filepath.Join(gitDir, p)); err != nil {
return fmt.Errorf("normalize .git/%s: %w", p, err)
}
}
return nil
}
// runGitEnv runs git in dir under a hermetic environment, returning its
// standard output. Errors carry the
// standard error stream, which is where git reports what went wrong.
func runGitEnv(ctx context.Context, dir string, args ...string) (string, error) {
gitArgs := make([]string, 0, len(gitEphemeralConfig)+len(args))
gitArgs = append(gitArgs, gitEphemeralConfig...)
gitArgs = append(gitArgs, args...)
cmd := exec.CommandContext(ctx, "git", gitArgs...)
cmd.Dir = dir
cmd.Env = []string{
"GIT_CONFIG_NOSYSTEM=1",
"HOME=/dev/null",View on GitHub (pinned to 82ba2681db)
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
Defensive patterns
Strategy: retry
Validate before calling
for _, p := range []string{"logs","hooks"} {
if _, err := os.Stat(filepath.Join(gitDir, p)); err != nil && !os.IsPermission(err) { continue }
if !isWritable(filepath.Join(gitDir, p)) { return fmt.Errorf("%s not writable", p) }
} Type guard
func isPathWritable(path string) bool {
f, err := os.CreateTemp(filepath.Dir(path), ".probe")
if err != nil { return false }
f.Close(); os.Remove(f.Name()); return true
} Try / catch
if err != nil {
var pe *fs.PathError
if errors.As(err, &pe) { /* check permissions on pe.Path */ }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- create pack temp dir: %w
- failed to read go.mod at %s: %w
- remove temporary octopus merge git repository: %w
- apply %s changes: %w
- convert %s to conflict markers: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/37dad4d09f1239da.
Report an issue: GitHub.