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
- Trim trailing directory separators and append the .csproj file name before calling
- Validate File.Exists(templateCsProj) at the call site to catch wrong paths early
- Pass the actual project file, not the folder containing it
- 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
- Trim trailing separators when building paths by string concatenation
- End every template path with the actual .csproj file name
- Validate with File.Exists before invoking the builder
- Prefer Path.Combine over manual string joining to avoid separator bugs
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
- Template csproj path should be absolute
- Value cannot be null. (Parameter 'onCompletedAsync')
- Value cannot be null. (Parameter 'onNext')
- Value cannot be null. (Parameter 'onError')
- Value cannot be null. (Parameter 'disposable')
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)