---
title: "Troubleshooting — Workflows for Laravel | Roundly"
description: "Every workflows:doctor finding with its fix, and every failure code — when it is written and whether a manual retry can help."
url: https://roundly-consulting.com/open-source/docs/workflows-for-laravel/troubleshooting
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Workflows for Laravel](https://roundly-consulting.com/open-source/docs/workflows-for-laravel.md)

# Troubleshooting

Start with php artisan workflows:doctor (add --json in CI, --strict to fail on warnings), then workflows:list --status=failed and workflows:show <ulid> for one instance’s path, step log, waits and next escalation.

## Doctor findings

Levels are the DoctorLevel enum (ok, warn, fail):

| Check | Level | Finding | Fix |
| --- | --- | --- | --- |
| key types | ok / fail | A registered subject’s key doesn’t fit subject\_key\_type. | Set workflows.subject\_key\_type (or key\_type) to the model’s key type before migrating. |
| owner key type | fail | The teams model’s key doesn’t fit owner\_key\_type. | Set workflows.owner\_key\_type to uuid / ulid. |
| queue | warn | Queue off, or the connection is sync. | Fine for local work; in production turn the queue on with a real connection. |
| queue timeout | fail | retry\_after ≤ the step job timeout. | Raise the connection’s retry\_after above engine.lease − 30 s, or lower engine.lease. |
| pcntl | warn | The worker can’t enforce the job timeout. | Install the pcntl extension on workers. |
| cache | warn | The default cache store is array or file. | Use a store every server shares (redis, database, memcached, dynamodb) for onOneServer(). |
| wake | warn | workflows:wake never ran, or last ran more than five minutes ago. | Schedule it every minute with withoutOverlapping()->onOneServer() and run the scheduler. |
| step types | ok / fail | The catalogue fails to load, or an active version uses a step type that is no longer registered. | Fix or remove the named class, register the type again, or publish a version without it. |
| config schema | fail | A host type’s configSchema() uses a keyword the validator doesn’t support. | Write it with ConfigSchema. |
| adapter | fail | An active definition uses a step type whose package is missing. | composer require roundly-consulting/<package>, or publish a version without the step. |
| calendar | fail | An active definition’s default calendar is missing. | Register it, or create the opening-hours calendar for the owner. |
| opening hours | fail | An active definition uses an opening\_hours: calendar but its owner model doesn’t use HasOpeningHours. | Add implements OpeningHoursOwner + use HasOpeningHours to the owner model (for teams: your swapped team model). |
| condition waits | warn | An active version waits on a condition of a subject type whose model doesn’t use HasWorkflows. | Add the trait, or accept that it re-checks on its poll only. |
| events | fail / warn | A mapped class doesn’t exist or '\*' isn’t a WorkflowEvent (fail); a mapped event carries no idempotency key, a class is mapped twice, or an on\_behalf\_of event definition’s mapping has no user (warn). | Fix the mapping; add idempotency\_key; keep one mapping; map user or run as system. |
| webhooks | ok / fail | The route is enabled without verify\_middleware (it stays unregistered). | Set workflows.webhooks.verify\_middleware, e.g. \['sentinel.signed:<profile>'\]. |
| stuck | warn | Running instances with nothing queued, running or waiting for longer than engine.lease. | workflows:show <ulid>; usually a lost job that wake re-pushes after engine.requeue\_after; otherwise workflows:retry or workflows:cancel. |
| wake backlog | warn | Waits or escalations due more than five minutes ago. | Check the scheduler and raise wake.batch. |
| bridges / failures | ok | The installed bridges; how many instances failed in the last 24 hours. | — |

## Failure codes

WorkflowInstance::failure() and StepRun::error() return the FailureCode case (a code:detail code by its prefix; FailureCode::detail() reads the detail) or, for a host step’s own code, the string. “Retry helps” means workflows:retry (or instances()->retry()) can succeed once the cause is gone — FailureCode::retryable(). The retry picks up where the step failed: a failed handle() runs again, and a failed resume() is resumed again with the signal it received (the event’s payload, the decision, the timeout), so an event that already arrived does not have to be sent again. Every code has an English and a Slovak message (FailureCode::message()).

| Code | Written when | Retry helps |
| --- | --- | --- |
| `adapter_missing` | A step needs a package that isn’t installed (approval, notify, transition, a form trigger). | yes, after installing it |
| `approval_cancelled` | The approval round was cancelled outside the workflow. | no |
| `approval_closed` | The approval round was already closed when the step acted on it. | no |
| `approval_missing` | The approval round no longer exists. | no |
| `authorization_denied` | The authorization hook refused the step (policy, abilities, the system actor). | yes, after granting it |
| `calendar_missing` | A business calendar the step needs is missing for the owner. | yes, after creating it |
| `calendar_never_open` | The calendar has no open time within the search window. | no (fix the calendar, then cancel and restart) |
| `child_not_started` | A start\_workflow child could not be started (retired, inactive, inputs not covered), or the start-rate limit refused it (retried automatically with backoff). | yes, after fixing the child definition (or once the rate window has room) |
| `delay_field_empty` | A delay with until\_field found the field empty. | yes, after filling the field |
| `ended_by_graph` | An end node with status: failed. | no |
| `exception` | A handler threw an unknown exception (its message is stored for operators, never rendered). | yes |
| `field_write_refused` | set\_field targeted a lifecycle state column. | no |
| `idempotency_conflict` | The lifecycle saw the same idempotency key for a different transition. | no |
| `invalid_approval_request` | Approvals refused the request (configuration). | no |
| `invalid_output` | A handler returned output that isn’t plain JSON or is over steps\_output.max\_bytes. | no (fix the handler) |
| `invalid_value` | set\_field got a value of the wrong type at run time. | no |
| `lease_expired` | A claimed step outlived its lease (a crashed or slow worker). | yes (retried automatically) |
| `missing_edge` | The next node does not exist. | no |
| `missing_outcome_edge` | A step ended with an outcome that has no edge. | no (publish a version with the edge) |
| `missing_trigger` | The version has no trigger. | no |
| `nesting_depth_exceeded` | start\_workflow went deeper than limits.max\_depth. | no |
| `no_approvers` | The approvers resolved to no one. | yes, after fixing the parties |
| `no_result` | A handler returned nothing. | no (fix the handler) |
| `notification_refused:<code>` | Notifications refused the notification permanently. | no |
| `owner_mismatch` | The subject no longer belongs to the workflow’s owner. | yes, if it moves back |
| `resume_unsupported` | A signal reached a step type that cannot resume. | no |
| `roles_unavailable` | A role party but no roles resolver (teams or parties.roles). | yes, after configuring one |
| `start_rate_exceeded` | The definition started more than limits.max\_starts\_per\_minute times in a minute. | no — no step ran; send the start again (its key was left free) |
| `step_limit_exceeded` | The instance ran more than limits.max\_steps steps. | no |
| `step_missing` | An escalation’s waiting step no longer exists. | no |
| `subject_missing` | The subject was deleted or is invisible in the owner context. | yes, if it is restored |
| `transition_conflict` | Another change reached the subject first (retried automatically). | yes |
| `transition_denied:<code>` | The lifecycle guard refused the transition (retried automatically only when the lifecycle says so). | no — unless what the guard checks changes |
| `transition_unusable` | The transition can’t be used on this subject. | no |
| `unknown_approval_preset` | The approval preset does not exist. | no |
| `unknown_escalation_action` | An escalation action type is not available. | no |
| `unknown_step_type` | The node’s step type is not registered (or the node is gone). | yes, after registering it |
| `version_missing` | The instance’s version no longer exists. | no |
| `worker_timeout` | The queue worker killed the job. | yes (retried automatically) |

Package exceptions that a step turns into a failure (a child start refused, for example) keep their own error codes — see Exceptions.

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/workflows-for-laravel.md)
