Troubleshooting
"No published version to preview" in the editor
The definition exists (the first-boot seeder created it) but no
email_templates row carries status='published'. Cause: the
first-boot seeder skipped the slug because an operator-edited draft
already existed. Fix: publish a draft from
/admin/email/templates/ or clear the
email.seeded_v1 admin setting and restart to re-run the seeder
against every slug that lacks any template version.
Every send for one slug is landing in email_deliveries with status='failed'
Open the failed row in /admin/email/deliveries/<id> and inspect
last_error:
email: template not found— the definition has noactive_version_id. See above.template … executing … undefined— a template references a field that's not in the supplied context. Checkcontext_snapshotvs the merge-field reference.5.1.1 user unknown/ any5.y.zresponse — the recipient's mailbox rejected the mail. The bounce classifier flags these as hard-bounces so the address lands on the suppression list automatically.
Unsubscribe link returns 410 Gone
Expected. 410 Gone is the RFC 8058 response for a token that's
missing, expired (> 30 days), or already consumed. The public
confirmation page at /email/unsubscribe renders the
already-consumed state for all three cases — this is intentional
to avoid an enumeration oracle.
Admin preview works but Gmail quarantines the real send
Deliverability is an SPF / DKIM / DMARC problem, not a template problem. See the checklist at dmarc.md.
Retention job deleted rows I wanted to keep
Increase email.context_retention_days in
/admin/settings — default 90, bounded to [7, 365]. The
worker runs daily at startup; the next run uses the updated
setting.
Bundle import fails with kind: "checksum"
The bundle's checksum field doesn't match the recomputed SHA-256
of the canonical templates array. Causes:
- Someone edited the JSON after export — recompute via the
shell one-liner in bundles.md and write the new
value back into
checksum. - The export came from a different
bundle_version(only1exists today).
"Template failed validation" when saving a draft
The save path runs engine.Parse + engine.Lint. Common cases:
- Referencing an undeclared namespace →
kind: "undeclared_namespace". Add the namespace torequired_namespaceson the definition (currently requires a DB edit) or rewrite the reference. - Calling a disallowed function →
kind: "unknown_function". See the sealed funcmap list in merge-fields.md. - Syntax error →
kind: "syntax". Go'stext/templateerror pointing at the offending line is surfaced verbatim.