{"record":{"id":"21d31b8f7e2ac901","repo":"stalwartlabs/stalwart","slug":"sieveevent-runtimeerror","errorCode":"SieveEvent::RuntimeError","errorMessage":"Empty query string","messagePattern":"Empty query string","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/scripts/plugins/query.rs","lineNumber":35,"sourceCode":"pub async fn exec(ctx: PluginContext<'_>) -> trc::Result<Variable> {\n    // Obtain store name\n    let store = match &ctx.arguments[0] {\n        Variable::String(v) if !v.is_empty() => ctx\n            .server\n            .get_lookup_store(v.as_str())\n            .and_then(|v| v.into_store()),\n        _ => Some(ctx.server.core.storage.data.clone()),\n    }\n    .ok_or_else(|| {\n        trc::SieveEvent::RuntimeError\n            .ctx(trc::Key::Id, ctx.arguments[0].to_string().into_owned())\n            .details(\"Unknown store\")\n    })?;\n\n    // Obtain query string\n    let query = ctx.arguments[1].to_string();\n    if query.is_empty() {\n        trc::bail!(\n            trc::SieveEvent::RuntimeError\n                .ctx(trc::Key::Id, ctx.arguments[0].to_string().into_owned())\n                .details(\"Empty query string\")\n        );\n    }\n\n    // Obtain arguments\n    let arguments = match &ctx.arguments[2] {\n        Variable::Array(l) => l.iter().map(to_store_value).collect(),\n        v => vec![to_store_value(v)],\n    };\n\n    // Run query\n    if query\n        .as_bytes()\n        .get(..6)\n        .is_some_and(|q| q.eq_ignore_ascii_case(b\"SELECT\"))\n    {","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/stalwartlabs/stalwart/blob/e96200385781a6a9995a8b839ac27d6c75a983ee/crates/common/src/scripts/plugins/query.rs#L17-L53","documentation":"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.","triggerScenarios":"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.","commonSituations":"Sieve variable interpolation producing an empty value (unset header, missing match), copy-pasted script examples with placeholder left blank.","solutions":["Fix the Sieve script to pass a non-empty query string as the second argument.","Check upstream variable assignments that feed the query argument and add a default (e.g. via `set`).","Guard the call with a test/exists check before executing the query action.","Verify plugin arguments order — argument[0] is the id, argument[1] must be the query."],"exampleFix":"// before (sieve)\nquery \"service\" \"${missing_var}\";\n// after (sieve)\nif test :not string :is \"${missing_var}\" \"\" {\n    query \"service\" \"${missing_var}\";\n}","handlingStrategy":"validation","validationCode":"// in the Sieve script, before calling the query action\nif test :not string :is \"${query}\" \"\" {\n    query \"service\" \"${query}\";\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default every variable used in query arguments with `set` fallbacks.","Validate Sieve scripts against the plugin's argument contract before deploy.","Test scripts with representative empty-header cases.","Keep query strings free of blank placeholders."],"tags":["sieve","scripting","validation"],"backgroundTag":"empty-required-field","analyzedSha":"e96200385781a6a9995a8b839ac27d6c75a983ee","analyzedAt":"2026-09-06T22:07:17.982Z","contentChangedAt":"2026-09-06T22:07:17.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}