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

  1. Fix the Fluid template syntax in the pickup directory base setting (close all {{ }} and {% %} tags).
  2. If you need literal braces, escape them rather than leaving raw '{{' or '{%' in the value.
  3. Use only documented variables: {{ AppData }} and ShellSettings properties.
  4. 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

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

Related errors


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)