OrchardCMS/OrchardCore · error · InvalidOperationException
A virtual path is required
Error message
A virtual path is required
What it means
TemporaryFileBuilder.SetFileAsync writes a deployment/export file under a virtual (root-relative) folder. The path must be relative; a subpath starting with '/' means an absolute virtual path was supplied, which cannot be combined with the builder's Folder, so it throws InvalidOperationException("A virtual path is required").
Solutions
- Strip the leading '/' (or use .TrimStart('/')) from the subpath before calling SetFileAsync.
- Normalize path building so subpaths are always constructed relative to the deployment folder root.
- Add a guard in your deployment source that validates subpath does not start with '/'.
Example fix
// before
await fileBuilder.SetFileAsync("/media/logo.png", stream);
// after
await fileBuilder.SetFileAsync(subpath.TrimStart('/'), stream); Defensive patterns
Strategy: validation
Validate before calling
if (string.IsNullOrEmpty(subpath) || subpath.StartsWith('/'))
throw new ArgumentException($"Subpath must be a relative virtual path, got '{subpath}'"); Type guard
bool IsRelativeSubpath(string p) => !string.IsNullOrEmpty(p) && !p.StartsWith('/'); Try / catch
catch (InvalidOperationException ex) when (ex.Message.Contains("virtual path")) { logger.LogError(ex, "Bad export path: {Path}", subpath); } Prevention
- Always TrimStart('/') paths destined for the deployment file builder
- Build export paths from relative segments, not absolute user paths
- Unit-test path building helpers for leading-slash input
When it happens
Trigger: Calling SetFileAsync with subpath like "/wwwroot/file.txt" or "/media/image.png" — i.e., a leading slash. Only relative subpaths ("media/image.png") are accepted.
Common situations: Custom deployment sources/steps building export file paths by concatenating a base path that already ends with '/'; using Path.Combine-style absolute paths from user input; recipes or import steps producing absolute virtual paths.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- A message body is required in order to send a message.
- A phone number is required in order to send a message.
- Both the index-name and index full-name must be set.
- Cannot add model validation errors when there's no Updater…
- Cannot add model validation errors when there's no Updater…
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/eaf6a4eaf7e9e5f8.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore.Deployment.Core/Services/TemporaryFileBuilder.cs:35
public string Folder { get; }
public void Dispose()
{
if (_deleteOnDispose)
{
if (Directory.Exists(Folder))
{
Directory.Delete(Folder, true);
}
}
}
public async Task SetFileAsync(string subpath, Stream stream)
{
if (subpath.StartsWith('/'))
{
throw new InvalidOperationException("A virtual path is required");
}
var fullname = PathExtensions.Combine(Folder, subpath);
var directory = new FileInfo(fullname).Directory;
if (!directory.Exists)
{
directory.Create();
}
using var fs = File.Create(fullname, 4 * 1024, FileOptions.None);
await stream.CopyToAsync(fs);
}
public override string ToString()
{
return Folder;View on GitHub (pinned to 4306c0717f)