stalwartlabs/stalwart · error

SieveEvent::RuntimeError

SieveEvent::RuntimeError

Error message

Empty query string

What it means

The Sieve `query` plugin's exec builds an HTTP/query request from script arguments; the second argument is the query string. If it is empty the runtime raises a SieveEvent::RuntimeError because issuing an empty query is meaningless and would fail downstream anyway.

Source

Thrown at crates/common/src/scripts/plugins/query.rs:35

pub async fn exec(ctx: PluginContext<'_>) -> trc::Result<Variable> {
    // Obtain store name
    let store = match &ctx.arguments[0] {
        Variable::String(v) if !v.is_empty() => ctx
            .server
            .get_lookup_store(v.as_str())
            .and_then(|v| v.into_store()),
        _ => Some(ctx.server.core.storage.data.clone()),
    }
    .ok_or_else(|| {
        trc::SieveEvent::RuntimeError
            .ctx(trc::Key::Id, ctx.arguments[0].to_string().into_owned())
            .details("Unknown store")
    })?;

    // Obtain query string
    let query = ctx.arguments[1].to_string();
    if query.is_empty() {
        trc::bail!(
            trc::SieveEvent::RuntimeError
                .ctx(trc::Key::Id, ctx.arguments[0].to_string().into_owned())
                .details("Empty query string")
        );
    }

    // Obtain arguments
    let arguments = match &ctx.arguments[2] {
        Variable::Array(l) => l.iter().map(to_store_value).collect(),
        v => vec![to_store_value(v)],
    };

    // Run query
    if query
        .as_bytes()
        .get(..6)
        .is_some_and(|q| q.eq_ignore_ascii_case(b"SELECT"))
    {

View on GitHub (pinned to e962003857)

Solutions

  1. Fix the Sieve script to pass a non-empty query string as the second argument.
  2. Check upstream variable assignments that feed the query argument and add a default (e.g. via `set`).
  3. Guard the call with a test/exists check before executing the query action.
  4. Verify plugin arguments order — argument[0] is the id, argument[1] must be the query.

Example fix

// before (sieve)
query "service" "${missing_var}";
// after (sieve)
if test :not string :is "${missing_var}" "" {
    query "service" "${missing_var}";
}
Defensive patterns

Strategy: validation

Validate before calling

// in the Sieve script, before calling the query action
if test :not string :is "${query}" "" {
    query "service" "${query}";
}

Prevention

When it happens

Trigger: A Sieve script calls the query plugin action with an empty string (or value that stringifies to empty) as its second argument, e.g. `query :..., ""` or a variable that was never assigned.

Common situations: Sieve variable interpolation producing an empty value (unset header, missing match), copy-pasted script examples with placeholder left blank.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/21d31b8f7e2ac901. Report an issue: GitHub.