{"record":{"id":"2ae749395849aae8","repo":"stalwartlabs/stalwart","slug":"failed-to-parse-calendar-template","errorCode":null,"errorMessage":"Failed to parse calendar template","messagePattern":"Failed to parse calendar template","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/config/groupware.rs","lineNumber":133,"sourceCode":"            max_ical_size: calendar.max_i_calendar_size as usize,\n            max_ical_instances: calendar.max_recurrence_expansions as usize,\n            max_ical_attendees_per_instance: calendar.max_attendees as usize,\n            max_vcard_size: book.max_v_card_size as usize,\n            vcard_version: match book.v_card_version {\n                RegistryVCardVersion::V3 => VCardVersion::V3_0,\n                RegistryVCardVersion::V4 => VCardVersion::V4_0,\n            },\n            max_file_size: file.max_size as usize,\n            alarms_enabled: alarm.enable,\n            alarms_minimum_interval: alarm.min_trigger_interval.into_inner().as_secs() as i64,\n            alarms_allow_external_recipients: alarm.allow_external_rcpts,\n            alarms_from_name: alarm.from_name,\n            alarms_from_email: alarm.from_email,\n            alarms_template: Template::parse(include_str!(concat!(\n                env!(\"CARGO_MANIFEST_DIR\"),\n                \"/../../resources/html-templates/calendar-alarm.html.min\"\n            )))\n            .expect(\"Failed to parse calendar template\"),\n            itip_enabled: sched.enable,\n            itip_auto_add: sched.auto_add_invitations,\n            itip_inbound_max_ical_size: sched.itip_max_size as usize,\n            itip_outbound_max_recipients: sched.max_recipients as usize,\n            itip_inbox_auto_expunge: dr\n                .expunge_scheduling_inbox_after\n                .map(|d| d.into_inner().as_secs()),\n            itip_http_rsvp_url: if sched.http_rsvp_enable {\n                if let Some(url) = sched\n                    .http_rsvp_url\n                    .as_deref()\n                    .map(|v| v.trim().trim_end_matches('/'))\n                    .filter(|v| !v.is_empty())\n                {\n                    Some(url.to_string())\n                } else {\n                    Some(format!(\"https://{}/calendar/rsvp\", system.default_hostname))\n                }","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/stalwartlabs/stalwart/blob/e96200385781a6a9995a8b839ac27d6c75a983ee/crates/common/src/config/groupware.rs#L115-L151","documentation":"This panic occurs during GroupwareConfig construction when the embedded, minified calendar-alarm HTML template fails Template::parse. Because include_str! bakes resources/html-templates/calendar-alarm.html.min into the binary at compile time, the .expect() should never fire in normal operation; if it does, the shipped template content is corrupt or incompatible with the current Template parser.","triggerScenarios":"Constructing GroupwareConfig (groupware settings enabled) where Template::parse on calendar-alarm.html.min returns Err — typically after hand-editing the minified template, running a broken minification, or a parser upgrade that rejects legacy template syntax.","commonSituations":"Developers editing the .min template directly and breaking placeholder syntax; build/packaging pipelines truncating files under resources/; distribution patches conflicting with a stricter template parser in a newer version; incomplete source checkouts.","solutions":["Restore the pristine resources/html-templates/calendar-alarm.html.min from the upstream repository","Regenerate the minified template with the project's official minify/build tooling instead of hand-editing","Run Template::parse on the file in a test to pinpoint the exact syntax error","Audit packaging steps (deb/docker) for anything rewriting or truncating resources/","Verify a complete source checkout; include_str! requires the file at compile time"],"exampleFix":"// before (hand-edited min template with broken placeholder)\n{{ alarm.start|bad_filter }}\n// after (restore upstream placeholder syntax)\n{{alarm.start}} {{alarm.end}} {{alarm.summary}}","handlingStrategy":"validation","validationCode":"// compile-time / CI check that the embedded template parses\n#[test]\nfn calendar_alarm_template_parses() {\n    Template::parse(include_str!(concat!(\n        env!(\"CARGO_MANIFEST_DIR\"),\n        \"/../../resources/html-templates/calendar-alarm.html.min\"\n    ))).expect(\"calendar-alarm template must parse\");\n}","typeGuard":null,"tryCatchPattern":"// Not catchable at runtime: it panics via .expect() during config load.\n// Guard at process startup:\nlet config = match Config::build(...) {\n    Ok(c) => c,\n    Err(e) | Err(_) if e.to_string().contains(\"Failed to parse calendar template\") => {\n        eprintln!(\"template resource corrupted; reinstall package\");\n        std::process::exit(1);\n    }\n};","preventionTips":["Never hand-edit *.html.min files; always regenerate via the official minifier","Add a unit test that parses every embedded template so breakage is caught in CI","Verify package integrity after builds (checksum resources/ contents)","When upgrading, re-run template minification against the current parser"],"tags":["panic","template","config","groupware"],"backgroundTag":"module-init-failed","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"}