MaterialDesignInXAML/MaterialDesignInXamlToolkit · error · InvalidOperationException

Did not find repository root

Error message

Did not find repository root

What it means

PathHelper.FindRepoRoot walks upward from the current working directory looking for a directory literally named '.git'; if the loop reaches the filesystem root without finding one it throws InvalidOperationException. This is an environment probe used by the MaterialDesign resource-generation codegen tool to anchor its hard-coded relative output paths (e.g. ..\..\..\..\MaterialDesignColors.Wpf\Themes). It throws purely because the process was started from outside any git checkout, so the tool cannot know where to write generated files.

Source

Thrown at src/MaterialDesignToolkit.ResourceGeneration/PathHelper.cs:20

public static class PathHelper
{
    private static readonly Lazy<string> _repoRoot = new(FindRepoRoot);
    public static string RepositoryRoot => _repoRoot.Value;


    private static string FindRepoRoot()
    {
        for (string? currentDirectory = Path.GetFullPath(".");
            !string.IsNullOrEmpty(Path.GetDirectoryName(currentDirectory));
            currentDirectory = Path.GetDirectoryName(currentDirectory))
        {
            if (Directory.Exists(Path.Combine(currentDirectory!, ".git")))
            {
                return currentDirectory!;
            }
        }
        throw new InvalidOperationException("Did not find repository root");
    }
}

View on GitHub (pinned to 98edec3a0b)

Solutions

  1. Run the generator with its working directory set to a folder inside the cloned repository (e.g. the src/MaterialDesignToolkit.ResourceGeneration project folder), not from /tmp, ~, or the bin/ output directory.
  2. Confirm a real .git directory exists in an ancestor of the working directory with 'git rev-parse --git-dir'; if that prints a path, .git is present.
  3. If you intentionally lack .git (archive build), make FindRepoRoot accept an override such as the MATERIALDESIGN_REPO_ROOT env var, or a '.repo-root' marker file, before falling back to the throw.
  4. If you are in a git worktree (where .git is a file), change the probe to also accept a .git file, or use LibGit2Sharp/Native git to resolve the root instead of Directory.Exists.
  5. Wrap the access to PathHelper.RepositoryRoot so the InvalidOperationException is caught and rethrown with the offending start path for faster diagnosis.

Example fix

// before
private static string FindRepoRoot()
{
    for (string? currentDirectory = Path.GetFullPath(".");
        !string.IsNullOrEmpty(Path.GetDirectoryName(currentDirectory));
        currentDirectory = Path.GetDirectoryName(currentDirectory))
    {
        if (Directory.Exists(Path.Combine(currentDirectory!, ".git")))
            return currentDirectory!;
    }
    throw new InvalidOperationException("Did not find repository root");
}

// after
private static string FindRepoRoot()
{
    var fromEnv = Environment.GetEnvironmentVariable("MATERIALDESIGN_REPO_ROOT");
    if (!string.IsNullOrWhiteSpace(fromEnv) && Directory.Exists(fromEnv))
        return Path.GetFullPath(fromEnv);

    for (string? currentDirectory = Path.GetFullPath(".");
        !string.IsNullOrEmpty(Path.GetDirectoryName(currentDirectory));
        currentDirectory = Path.GetDirectoryName(currentDirectory))
    {
        var git = Path.Combine(currentDirectory!, ".git");
        // accept directory, git-worktree pointer file, or a marker file for archive builds
        if (Directory.Exists(git) || File.Exists(git) ||
            File.Exists(Path.Combine(currentDirectory!, ".repo-root")))
            return currentDirectory!;
    }
    throw new InvalidOperationException(
        $"Did not find repository root starting from '{Path.GetFullPath(".")}'. " +
        "Run from inside the repo checkout or set MATERIALDESIGN_REPO_ROOT.");
}
Defensive patterns

Strategy: validation

Validate before calling

string cwd = Path.GetFullPath(".");
bool foundGit = false;
for (string? d = cwd; !string.IsNullOrEmpty(Path.GetDirectoryName(d)); d = Path.GetDirectoryName(d))
{
    if (Directory.Exists(Path.Combine(d!, ".git")) || File.Exists(Path.Combine(d!, ".git")))
    {
        foundGit = true;
        break;
    }
}
if (!foundGit)
    throw new InvalidOperationException(
        $"Not running inside a git checkout (cwd={cwd}). cd into the repo or set MATERIALDESIGN_REPO_ROOT.");

Type guard

// predicate a caller can check before touching PathHelper.RepositoryRoot
static bool IsInsideRepo(string path) =>
    EnumerateAncestors(Path.GetFullPath(path)).Any(d =>
        Directory.Exists(Path.Combine(d, ".git")) || File.Exists(Path.Combine(d, ".git")));

static IEnumerable<string> EnumerateAncestors(string dir)
{
    for (string? d = dir; !string.IsNullOrEmpty(Path.GetDirectoryName(d)); d = Path.GetDirectoryName(d))
        yield return d!;
}

Try / catch

try
{
    var root = PathHelper.RepositoryRoot;
}
catch (InvalidOperationException ex) when (ex.Message.Contains("repository root"))
{
    // report cwd + hint rather than letting the generic throw propagate
    throw new InvalidOperationException(
        $"{ex.Message} (search started from '{Path.GetFullPath(".")}')", ex);
}

Prevention

When it happens

Trigger: Running the ResourceGeneration console app with a working directory that is not inside the repository tree, running it from a source tarball/zip export that omits the .git folder, or running it under CI that does a shallow/git-archive checkout without a .git directory. The check is Directory.Exists(<dir>/.git), so a worktree whose .git is a file (git worktree) instead of a directory will also fail this test.

Common situations: Running dotnet run on the generator from the wrong folder; copying the source tree without the .git metadata; building in a sandbox/container where the source was rsync'd without hidden dirs; using 'git archive' or downloading the GitHub ZIP (neither includes .git); switching to a git worktree where .git is a pointer file rather than a folder.

Related errors


AI-assisted analysis of MaterialDesignInXAML/MaterialDesignInXamlToolkit@98edec3a0b (2026-08-13). Data as JSON: /api/errors/d6b7f209f1c13439. Report an issue: GitHub.