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

  1. Check filesystem permissions on the reconstructed .git directory and ensure the engine process can delete entries
  2. Remove any immutable flags (chattr -i) or read-only mounts from the git dir
  3. Stop processes holding locks on .git (e.g. another git invocation) and clear stale lock files
  4. 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

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


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/37dad4d09f1239da. Report an issue: GitHub.