dotnet/reactive · error · ArgumentException

Template csproj path should refer to a file, not a directory

Error message

Template csproj path should refer to a file, not a directory

What it means

Immediately after deriving the folder, CreateModifiedProjectClone calls Path.GetFileName(templateCsProj); if that returns null (or the path referred to a directory), it throws ArgumentException('Template csproj path should refer to a file, not a directory'). The clone step needs the file name to copy the project into the temporary clone folder.

Solutions

  1. Trim trailing directory separators and append the .csproj file name before calling
  2. Validate File.Exists(templateCsProj) at the call site to catch wrong paths early
  3. Pass the actual project file, not the folder containing it
  4. Fix the caller's path-composition code so it always ends with a file name

Example fix

// before
string template = Path.Combine(root, "Templates", "Lib"); // directory
// after
string template = Path.Combine(root, "Templates", "Lib", "Lib.csproj");
Defensive patterns

Strategy: validation

Validate before calling

templateCsProj = templateCsProj.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (Directory.Exists(templateCsProj) || !templateCsProj.EndsWith(".csproj"))
    throw new ArgumentException("Expected a .csproj file path", nameof(templateCsProj));

Type guard

bool IsCsprojFile(string? p) => !string.IsNullOrWhiteSpace(p) && p.EndsWith(".csproj") && File.Exists(p);

Try / catch

try
{
    await builder.BuildAppAsync(templateCsProj, ...);
}
catch (ArgumentException ex) when (ex.Message.Contains("refer to a file"))
{
    // append the project file name and retry
}

Prevention

When it happens

Trigger: templateCsProj ends with a directory separator or names a directory (e.g., 'C:\repo\Templates\' or a trailing slash), so Path.GetFileName cannot yield a file name.

Common situations: Caller concatenated the path with a trailing separator; passed a directory path where a file path was expected; template path built by string joining ending in '/' or '\\'.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/dfd107929848d43f. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Test/Gauntlet/RxGauntlet.Common/Build/ComponentBuilder.cs:78

                ..(additionalPackageSources ?? [])
            ];

        (string projectTemplateFileName, ModifiedProjectClone project) = CreateModifiedProjectClone(
            appBuildTempFolderName, templateCsProj, modifyProjectFile, packageSourcesIncludingDynamicallyBuiltPackages);

        return await project.RunDotnetBuild(projectTemplateFileName);
    }

    private (string ProjectTemplateFileName, ModifiedProjectClone ProjectClone) CreateModifiedProjectClone(
        string tempParentFolderName,
        string templateCsProj,
        Action<ProjectFileRewriter> modifyProjectFile,
        (string FeedName, string FeedLocation)[]? additionalPackageSources)
    {
        string projectTemplateFolder = Path.GetDirectoryName(templateCsProj)
            ?? throw new ArgumentException("Template csproj path should be absolute", nameof(templateCsProj));
        string projectTemplateFileName = Path.GetFileName(templateCsProj)
            ?? throw new ArgumentException("Template csproj path should refer to a file, not a directory", nameof(templateCsProj));
        var projectClone = ModifiedProjectClone.Create(
            projectTemplateFolder,
            tempParentFolderName,
            modifyProjectFile,
            additionalPackageSources);
        _projectClones.Add(projectClone);

        return (projectTemplateFileName, projectClone);
    }

    public void Dispose()
    {
        foreach (ModifiedProjectClone clone in _projectClones)
        {
            clone.Dispose();
        }

        _projectClones.Clear();

View on GitHub (pinned to 94b5d5ab91)