OrchardCMS/OrchardCore · error · InvalidOperationException
Failed to parse SMTP pickup directory location base
Error message
Failed to parse SMTP pickup directory location base: {string.Join(System.Environment.NewLine, errors)} What it means
The pickup directory base supports a Fluid template (with ShellSettings and AppData values). ParseAndFormat parses the template with FluidParser and throws InvalidOperationException when TryParse fails, joining the parser errors into the message. The configured template has invalid Fluid syntax.
Solutions
- Fix the Fluid template syntax in the pickup directory base setting (close all {{ }} and {% %} tags).
- If you need literal braces, escape them rather than leaving raw '{{' or '{%' in the value.
- Use only documented variables: {{ AppData }} and ShellSettings properties.
- Validate the template with a Liquid parser/preview before saving the settings.
Example fix
// before (unclosed tag)
"{{ ShellSettings.Name/Pickup"
// after
"{{ AppData }}/{{ ShellSettings.Name }}/Pickup" Defensive patterns
Strategy: validation
Validate before calling
var parser = new FluidParser();
if (!parser.TryParse(template, out _, out var errors))
throw new InvalidOperationException("Invalid Fluid template: " + string.Join(Environment.NewLine, errors)); Try / catch
try { var path = ParseAndFormat(template, ...); } catch (InvalidOperationException ex) when (ex.Message.StartsWith("Failed to parse SMTP pickup directory")) { /* show parser errors in the settings UI */ } Prevention
- Test templates with a Liquid parser before saving.
- Escape literal braces in paths rather than leaving raw '{{' or '{%'.
- Use only the documented variables (AppData, ShellSettings).
When it happens
Trigger: The pickup directory location base setting contains a malformed Liquid/Fluid template, e.g. unclosed {{ or {% tag, wrong filter syntax; ParseAndFormat runs while resolving the SMTP pickup directory.
Common situations: Admin hand-edits the base path and accidentally types '{{' or '{%' characters that are meaningful to Fluid (e.g. Windows paths or placeholders spelled incorrectly), causing the parser to reject it.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- The SMTP pickup directory location is invalid.
- The SMTP pickup directory location
- An implementation of 'LiquidTemplateContext' is required
- An implementation of 'LiquidTemplateContext' is required
- An implementation of 'LiquidTemplateContext' is required
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/4b29e386b33e46d1.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.Email.Smtp/Services/SmtpPickupDirectoryResolver.cs:161
private static bool IsWindowsDriveQualifiedPath(string pickupDirectoryLocation)
=> pickupDirectoryLocation.Length >= 3 &&
char.IsAsciiLetter(pickupDirectoryLocation[0]) &&
pickupDirectoryLocation[1] == ':' &&
pickupDirectoryLocation[2] is '\\' or '/';
private static string NormalizeDirectorySeparators(string path)
=> path?
.Replace('\\', Path.DirectorySeparatorChar)
.Replace('/', Path.DirectorySeparatorChar);
private static string ParseAndFormat(string template, FluidParser fluidParser, ShellOptions shellOptions, ShellSettings shellSettings)
{
var templateOptions = new TemplateOptions();
templateOptions.MemberAccessStrategy.Register<ShellSettings>();
if (!fluidParser.TryParse(template, out var parsedTemplate, out var errors))
{
throw new InvalidOperationException($"Failed to parse SMTP pickup directory location base: {string.Join(System.Environment.NewLine, errors)}");
}
var templateContext = new TemplateContext(templateOptions);
templateContext.SetValue("AppData", shellOptions.ShellsApplicationDataPath);
templateContext.SetValue("ShellSettings", shellSettings);
return parsedTemplate.Render(templateContext, NullEncoder.Default)
.ReplaceLineEndings(string.Empty)
.Trim();
}
private static bool IsWithinBasePath(string pickupDirectoryLocationBase, string pickupDirectoryLocation)
{
var relativePath = Path.GetRelativePath(pickupDirectoryLocationBase, pickupDirectoryLocation);
return relativePath == "." ||
(!relativePath.Equals("..", StringComparison.Ordinal) &&
!relativePath.StartsWith($"..{Path.DirectorySeparatorChar}", StringComparison.Ordinal) &&View on GitHub (pinned to 4306c0717f)