Header missing from sieve scripting

Issue Description

When a sieve condition is using the header variable, user is prented with the error

Error parsing 'if' expression in condition #1: Invalid variable or constant "header"

This is unexpected behavour under RFC 5228 5.7

Expected Behavior

header to be accessible in global variables and conditions

Actual Behavior

Error parsing 'if' expression in condition #1: Invalid variable or constant "header"

Reproduction Steps

Create a condition to evaluate a header. Example
header :contains "X-Test" ["value"]

Observe invalid variable error

Relevant Log Output

Error parsing 'if' expression in condition #1: Invalid variable or constant "header"

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

headers are useful for filtering, classifying or flagging emails in any stage of email process.

As I was attempting to create a outbound strategy based on a header. This is set by a service sending emails and I want to route those specific emails to a seperate outbound strategy than other emails.

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Let me look into this issue with the Sieve header test.

The key insight is clear. The error “Invalid variable or constant ‘header’” is coming from Stalwart’s expression engine (tokenizer.rs), not from the Sieve interpreter itself. This means the script is accidentally being parsed as a Stalwart configuration expression rather than as a proper Sieve if test.

The header keyword is a perfectly valid Sieve test per RFC 5228 §5.7, and Stalwart’s examples confirm it works (e.g. if header :contains "List-Id" "@"). The error message is characteristic of the Stalwart expression tokenizer rejecting header as an unknown identifier – which means the condition is being evaluated through the eval expression path instead of through the standard Sieve if test path.

Most likely causes:

  1. The script is not a proper Sieve script at all – it may have been entered as a Stalwart filter expression (the configuration expression language) rather than a Sieve script. In that context header is not a known variable.

  2. Wrapping a Sieve header test inside an if eval "..." block – e.g. if eval "header :contains X-Test ['value']" is wrong. The eval test takes an expression, not a Sieve test. The header test must be used as a bare if test:

    require ["fileinto"];
    if header :contains "X-Test" "value" {
        fileinto "INBOX/Flagged";
    }
    
  3. Missing require statement – although this typically produces a different error, ensure the script starts with a valid require block if any extensions are needed.

The correct usage is to write a standard Sieve script and store it as a SieveSystemScript (trusted, for SMTP stages) or SieveUserScript (user-level filtering). Do not put Sieve tests like header inside if eval "..."eval is only for Stalwart’s arithmetic/logical expression language.

Relevant docs:

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Yes this specifically.

I need to be able to use header in a filter expression.

You cannot use Sieve commands in expressions. Expression are simple instructions with their own DSL. Read the Expressions documentation for details.

But what about my issue?

I need to be able to filter emails during the delivery strategy based on a header.

Use an actual Sieve script or an MTA hook for that.

MTA Hooks cant change the delivery strategy of the email and I see no way to have a sieve script run to do the same during the stratergy stage.