You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/failures-and-recovery.md
+43Lines changed: 43 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -116,6 +116,23 @@ The background task watchdog also scans for non-terminal runs with expired deadl
116
116
117
117
Waterline surfaces `failure_category` in the exceptions table as a dedicated **Category** column and in timeline failure detail entries. History exports include `failure_category` in the `failures[*]` array. Older failure rows that predate this classification have `failure_category = null` and are treated as unclassified in projections.
118
118
119
+
To backfill `failure_category` and `non_retryable` on older failure rows:
`Workflow\V2\Activity` defaults to `$tries = 1`, so an activity failure is sent back to the workflow immediately unless the activity opts into retry attempts.
@@ -146,6 +163,32 @@ The retry task records `retry_of_task_id`, `retry_after_attempt_id`, `retry_afte
146
163
147
164
`Workflow\Exceptions\NonRetryableExceptionContract` still short-circuits the retry policy: throwing a non-retryable exception fails the activity execution immediately and resumes the workflow with the exception.
148
165
166
+
### Non-retryable failure markers
167
+
168
+
When an activity or workflow throws an exception that implements `Workflow\Exceptions\NonRetryableExceptionContract`, the engine records a `non_retryable = true` flag on the `WorkflowFailure` row and in the typed history event payload (`ActivityFailed`, `WorkflowFailed`, `UpdateCompleted`). This durable marker communicates to operators, external workers, and tooling that the failure is permanent — retrying the same operation will not succeed.
-**History events**: `non_retryable` field in the typed event payload.
174
+
-**Failure snapshots**: `non_retryable` included in `FailureSnapshots::forRun()`.
175
+
-**Run detail view**: `non_retryable` in the exceptions array.
176
+
-**Timeline entries**: `non_retryable` in failure detail metadata.
177
+
-**History exports**: `non_retryable` in the `failures[*]` array.
178
+
-**Waterline**: a "non-retryable" badge next to the failure category in the exceptions table and timeline.
179
+
-**External worker bridge**: the `complete()` command payload accepts `non_retryable` so external workflow workers can report non-retryable failures without requiring the host process to resolve the throwable class.
180
+
181
+
For failures that do not implement the contract, `non_retryable` is `false` by default. The `php artisan workflow:v2:backfill-failure-categories` command can detect and backfill the flag for older failure rows whose recorded exception class implements `NonRetryableExceptionContract` in the current codebase.
182
+
183
+
```php
184
+
use Workflow\Exceptions\NonRetryableExceptionContract;
185
+
186
+
class PaymentDeclinedException extends \RuntimeException implements NonRetryableExceptionContract
187
+
{
188
+
// This failure will be marked as non-retryable in the durable record.
189
+
}
190
+
```
191
+
149
192
## Workflow-Level Retry
150
193
151
194
Durable Workflow v2 does **not** support automatic workflow-level retry. When a workflow run fails — whether from an unhandled exception, a structural limit, or a timeout — the run is terminal. The engine does not automatically start a new run of the same workflow instance.
0 commit comments