elsa-workflows/elsa-core · error · InvalidOperationException

Python.NET workflow expression execution is disabled. Set…

Error message

Python.NET workflow expression execution is disabled. Set PythonOptions.AllowHostCodeExecution to true only for trusted workflow authors; Python.NET is not a sandbox.

What it means

The Python.NET evaluator refuses to execute workflow expressions unless PythonOptions.AllowHostCodeExecution is explicitly true, because Python.NET gives scripts full host access and is not a sandbox. The throw is a security guard added by Elsa to prevent untrusted workflow authors from executing arbitrary host code.

Solutions

  1. Set PythonOptions.AllowHostCodeExecution to true only if workflow authors are fully trusted: builder.Services.Configure<PythonOptions>(o => o.AllowHostCodeExecution = true;)
  2. If workflows must not run privileged code, rewrite the expressions in a sandboxed language (JavaScript with sandboxing or C#) instead of enabling the flag
  3. Confirm the configuration binding actually reaches the options (e.g. configuration section key casing) so the flag is true at evaluation time

Example fix

// before
builder.Services.AddPython();
// after (trusted authors only)
builder.Services.AddPython();
builder.Services.Configure<PythonOptions>(o => o.AllowHostCodeExecution = true);
Defensive patterns

Strategy: validation

Validate before calling

// at startup, fail fast if the flag is required but unset
if (usesPythonExpressions && !pythonOptions.AllowHostCodeExecution)
    throw new InvalidOperationException("Enable PythonOptions.AllowHostCodeExecution for trusted authors.");

Try / catch

try { await evaluator.EvaluateAsync(expr, typeof(object), ctx, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("AllowHostCodeExecution"))
{ logger.LogWarning("Python execution disabled; reconfigure options."); }

Prevention

When it happens

Trigger: Evaluating any Python expression via PythonNetPythonEvaluator.EvaluateAsync while PythonOptions.AllowHostCodeExecution is false/unset (the safe default).

Common situations: Deploying an app that uses Python expressions without opting into host code execution, or upgrading Elsa to a version that introduced this opt-in flag and existing workflows stop running.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/b601a7401dad0263. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Expressions.Python/Services/PythonNetPythonEvaluator.cs:43

    /// </summary>
    public PythonNetPythonEvaluator(INotificationSender notificationSender) : this(notificationSender, Microsoft.Extensions.Options.Options.Create(new PythonOptions()))
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="PythonNetPythonEvaluator"/> class.
    /// </summary>
    public PythonNetPythonEvaluator(INotificationSender notificationSender, IOptions<PythonOptions> options)
    {
        _notificationSender = notificationSender;
        _options = options;
    }

    /// <inheritdoc />
    public async Task<object?> EvaluateAsync(string expression, Type returnType, ExpressionExecutionContext context, CancellationToken cancellationToken = default)
    {
        if (!_options.Value.AllowHostCodeExecution)
            throw new InvalidOperationException("Python.NET workflow expression execution is disabled. Set PythonOptions.AllowHostCodeExecution to true only for trusted workflow authors; Python.NET is not a sandbox.");

        using var gil = Py.GIL();
        using var scope = Py.CreateScope();
        var notification = new EvaluatingPython(scope, context);
        
        scope.Import("System");

        // Add globals.
        scope.Set("execution_context", new ExecutionContextProxy(context));
        scope.Set("input", new InputProxy(context));
        scope.Set("output", new OutputProxy(context));
        scope.Set("outcome", new OutcomeProxy(context));

        await _notificationSender.SendAsync(notification, cancellationToken);
        var wrappedScript = WrapInExecuteScriptFunction(expression);
        scope.Exec(wrappedScript);
        var result = scope.Get<object>(ReturnVarName);
        return result.ConvertTo(returnType);

View on GitHub (pinned to fe9217bdfa)