Troubleshooting
Where to look first
Start with the simulator. Enter the order (or the contact's email), click Simulate, and it tells you which workflows would run and, for the rest, the reason: Lyfe switched off, workflow disabled, Pro required, the trigger's own settings, a condition that failed with its actual value, or a run limit.
Then, depending on what you are looking at:
| Screen | Answers |
|---|---|
| Lyfe → Activity | Did a run start? Open it to see what each step did and why a run stopped or failed. |
| Lyfe → Messages | Was the email or text sent, refused by the mail server, suppressed by consent, or only simulated? |
| Lyfe → Log | Things that happened outside any run: a workflow skipped because it needs Pro, an error caught during a checkout, a scan's results, a missing layout template. |
| The order's edit screen in Commerce | Lyfe's runs for that order and the customer's recent messages. |
Errors are also written to Craft's own logs under the lyfe category, so they reach
storage/logs and anything you ship those logs to, even if Lyfe's log table itself is the problem.
A workflow did not start
Work down this list. The simulator checks the first five for you.
Lyfe is switched off. Enabled in Lyfe's settings is the master switch.
The workflow is not enabled. Presets always install disabled.
It needs Pro and the install is on Lite. After a downgrade, a workflow with a Pro trigger, a Pro step, a run limit, a cooldown or a send window refuses to start. The log has an entry saying "Skipped ... it needs the Pro edition." Remove the Pro parts or move back to Pro. On Lite, the workflow list will not let you enable such a workflow.
The trigger's own settings rule it out. A status trigger listening for shipped ignores an
order that moved into processing. A product trigger with no products or SKUs set never fires.
A cart trigger with Only carts with an email address on ignores a cart with no email.
A condition did not match. The simulator shows every rule with the value it actually saw. Watch for:
- "Days since last order" and "Days since first order" have no value for a contact who has never ordered, so "at least 120" does not match them.
- A numeric condition with nothing to compare fails. An order condition on a workflow started from a contact screen, a tag or a scan has no order to look at.
- Is the customer's first order needs a contact. An order with no email address has none.
It already ran for this event. Each workflow runs once per order, per status change, per refund and so on (the full list is in Usage). If you changed a workflow after it ran for an order, the same order will not start it again. To try again, simulate, or use a different order.
The contact is over the run limit or inside the cooldown (Pro). A manual start does not get around these either; they exist to protect the customer.
The order belongs to a different store. A workflow restricted to one store ignores orders from the others.
Workflows are triggering each other. A chain more than three workflows deep is refused, and the log says so.
Order status changes made outside Commerce's usual path. The status trigger listens to Commerce's order history. A status written directly to the database by an import script records no history and starts nothing.
A run started but a step did not happen
Open the run under Activity. Each step shows its outcome and message.
It is still waiting. A run's next step is shown with its due time. Steps after a delay only
run when something processes them, which is the cron job (or the queue, with Also process during
web requests). Check php craft lyfe/run/status: a growing "Due right now" count means nothing
is processing. See Installation. With web-request processing, Craft's
queue also has to be running.
It was abandoned as stale. A step that came due more than Abandon steps overdue by more than hours ago (72 by default) is recorded as "Abandoned: this step came due more than ... hours ago" instead of being sent late. This usually means cron stopped for a while.
The conditions no longer matched. With Re-check conditions before every step on, the run is dropped if the customer no longer qualifies. The run shows "Conditions no longer match, so the rest of the run was dropped." This is often correct: they refunded, unsubscribed or bought again.
It is outside the send window (Pro). The run waits until the window next opens, in the site's timezone. Check the site timezone in Craft's settings if the times look off by some hours.
An earlier step stopped the run. Stop the run if... does this on purpose. So does an email to an unsubscribed contact or an SMS to a contact who has not opted in: the refusal stops the run, so the steps after it never run. The run shows the reason as its last entry.
A Pro step was skipped. On Lite, a run that reaches a Pro action records it as skipped: "needs the Pro edition."
The step failed. Failed steps are retried (3 attempts, 15 minutes apart by default) and then the run is marked failed with the last error. Fix the cause, then use Run now on the run, which retries straight away with a fresh attempt count.
An email was not sent
Find it under Messages, or on the run.
| Status | Meaning | What to do |
|---|---|---|
| Suppressed | The contact has unsubscribed from marketing email (or not opted in to SMS). | Correct behaviour. If the email is genuinely transactional, turn on Transactional on the step. There is no override for SMS. |
| Simulated | The workflow, or the whole install, is in dry run. | Turn off Dry run on the workflow, or Global dry run in the settings. lyfe/run/status shows whether global dry run is on. |
| Failed | Craft's mailer refused it. The error is on the message. | Check Craft's own email settings with Test email in Lyfe's settings. |
| No message at all | The step did not run, failed while rendering its Twig, or had no address. | See the run, which has the error. "No email address to send to." means neither the contact, the order nor the user had one. |
If the email arrived without your layout, the log has a warning that the Email layout template
does not exist or failed to render. The path is relative to your site's templates/ folder.
An SMS was not sent
- "Twilio is not configured." or "... is not configured." One of the transport's credentials is empty, or is an environment variable that is not defined on this environment.
- "Twilio answered 4xx: ..." Twilio's own error code and message follow. A 401 is a credential problem; otherwise Twilio's message usually names the problem with the number.
- "The gateway answered ..." Your HTTP gateway returned something other than 2xx. The first 300 characters of its response are included.
- Suppressed. The contact has not opted in to SMS. New contacts are not opted in by default.
- "No phone number to text." The contact has no phone number. Lyfe only takes one from an order
address if your address field layout has a field with the handle
phone; otherwise add numbers on the contact screen, or set To on the step. - A local number went to the wrong country. Numbers without a
+or00get Default country code in front. Store numbers in international form if you sell across borders.
If the gateway rejects your credentials and the Authorization header uses an environment
variable, check that the variable holds the whole header value. Bearer $TOKEN is sent literally;
see SMS.
Test SMS in the settings uses the saved settings, not unsaved changes on screen.
A step fails saying its Twig can't run
The message is "This step was last changed by someone who isn't an admin, and Craft's Twig sandbox is off, so its Twig can't run."
The step is untrusted: someone who is not an admin added or changed it, and no admin has approved
it. With Craft's Twig sandbox off (or on Craft before 5.9), an untrusted step cannot run Twig. Any
{ counts, because Craft's object-template shorthand such as {firstName} runs too.
Either:
- an admin opens the workflow, reads the step (it has an orange Written by someone who isn't an admin badge), ticks Approve: run with full Twig and $ENV values on it, and saves; or
- turn on
enableTwigSandboxinconfig/general.php(Craft 5.9 or later), and untrusted steps render inside the sandbox instead.
Steps that existed before step trust was introduced in Lyfe start untrusted. Approve them the same way.
See Who can write what for the full model.
A step renders wrongly in the sandbox
An untrusted step with the sandbox on gets plain values, not Commerce objects. Method calls such as
order.getShortNumber(), craft.*, and filters the sandbox does not allow (including
commerceCurrency) are refused, and the step fails with Craft's sandbox error.
Use the plain values instead: order.shortNumber, order.totalPriceAsCurrency, and
item.totalAsCurrency inside order.lineItems. The full list is in
Usage. If the step needs more than that, an admin can approve it.
"Couldn't save the workflow"
The error is shown against the step concerned.
- "Only an admin can add or change a webhook step." Webhook steps must be written by an admin. A non-admin can leave an admin's webhook step untouched and save the rest of the workflow.
- "Only an admin can write Twig in a step while Craft's Twig sandbox is off. Use plain text, or
ask an admin." Remove every
{from the step, ask an admin to write it, or turn on the sandbox. This includes the placeholder copy in presets, which uses Twig for the customer's name. - "Use 24-hour HH:MM." The send window times must look like
09:00. - "The send window has to be longer than nothing." From and To are the same time.
- "Unknown condition ..." A condition's handle no longer exists, usually because a module that registered it has been removed.
A webhook step fails with "Only an admin can set up a webhook step"
The step is untrusted. An admin approves it in the workflow editor with Approve: run with full
Twig and $ENV values. A header value written as $VARIABLE also only resolves in a trusted step,
and only when the whole value is the variable.
A scheduled scan finds nobody
- Scans are Pro and run as part of
lyfe/run/due. Without cron, they never run. - A scan runs once per period (daily, weekly or monthly) from when it last ran. Running
lyfe/run/dueagain the same day does nothing for a daily scan. - The prefilter works on the order figures stored on each contact. After installing Lyfe, run
php craft lyfe/contacts/backfillso your existing customers exist as contacts, andphp craft lyfe/contacts/sync-statsafter a bulk import of orders. - Repeat for the same contact every left empty means each contact is picked up at most once, ever. A contact who has been through the workflow will not be again.
- Every contact that passes the prefilter is checked against your conditions, in batches of 500. On a large store, a prefilter that matches the conditions keeps each scan quick.
- The log records each scan with its candidate and started counts. Filter it by the
scanscategory.
Abandoned carts are not being detected
- Cart tracking is Pro, and Track carts must be on.
- The sweep runs with
lyfe/run/due. Use Check for abandoned carts now on the Carts screen to run it by hand (it needs the Run the abandoned-cart sweep permission). - A cart is only considered after Consider a cart abandoned after minutes without activity, and only with at least Ignore carts with fewer than line items.
- The Abandoned cart recovery preset only fires for carts with an email address. A guest who never reached the email step of checkout cannot be emailed.
{{ cartRestoreUrl }}is empty for a cart that is not tracked, or that has already become an order.
Customers keep getting chase emails after buying
Install and enable Stop chasing recovered carts alongside Abandoned cart recovery. It cancels pending runs when an order is placed. Out of the box it cancels the contact's pending runs of every other workflow; tick specific workflows if that is too broad.
Cancel this contact's pending runs refers to workflows by handle, so changing a workflow's handle means re-ticking it in any cancel step that points at it.
A discount code was empty in the email
{{ data.discountCode }} is set by Generate a discount code, so the email has to come after
that step in the same workflow. The code is kept with the run, so a delay between them is fine.
In a simulation, the code is invented but never created. See
Generate a discount code.
Codes stay valid after the "expiry" date Lyfe calculates, because Commerce coupons have no expiry of their own. Set an end date on the discount if codes must stop working.
Settings are missing from the nav
Lyfe → Settings appears only for admins, and only where allowAdminChanges is on. On
production, change settings in development and deploy them with project config, or set them in
config/lyfe.php.
The Carts screen appears only on Pro, and only for users with View tracked carts.