The short version.
- Decide, for every card that changes something, what happens when it fails: retry, take another path, or stop. The default is stop, and nobody is told.
- A 429 is an instruction to wait. Retry single calls after a delay, and make loops do less per run.
- A helper flow should catch its own errors and return a status. The flow that called it often cannot see them.
- Search before you read: a missing user should be an empty list, not an error.
- Write every failure to a table and alert from the table.
1. What happens when a card fails
By default a failed card stops the flow. The run is marked failed in Execution History and that is all: no message, no retry, and the users further down the list are never processed. For a leaver flow that means access left in place.
You have two tools. Each action card has its own error handling setting, where you can retry a number of times with a delay or send the failure somewhere else. And the If Error card wraps a group of cards: if any of them fails, its second branch runs and receives the error. Use the card setting for one call that may briefly fail; use If Error where a failure needs a decision.
2. A 429 means wait
A 429 Too Many Requests can come from Okta’s own API, from the application you are calling, or from the pace of your own flows. Treat it as “try again later”, never as “this user failed”.
- One call. Set the card to retry with a delay. In our experience, three attempts a few seconds apart clear most short bursts.
- A loop. Slow the loop down. Read a page of records, process that page, save where you stopped in a table, and let the next scheduled run continue. A loop that tries 5,000 users in one run is the usual cause of its own 429s.
- A raw API call. You handle the response yourself. Where the card returns the status code, branch on 429; where it fails instead, catch it with If Error and read the status from the error. The rate-limit headers in the response say when the limit resets.
Some connector cards wait and retry a 429 on their own. Do not assume yours does: check the card’s documentation and test it.
Make every step safe to repeat. Adding a user to a group twice is harmless; sending an email twice is not, so record what was sent before you retry.
3. Helper flows hide failures
When a flow hands each item to a helper flow, a failure inside the helper belongs to the helper’s run. Depending on how it was called, the parent may stop at that item or may never know. Either way the remaining items are at risk.
The pattern that holds: the helper wraps its work in If Error and always returns a status (ok or failed, with the reason). The parent counts the failures and records them. A helper that is started without waiting for its answer must log its own failures, because nothing else will.
4. Four failures to plan for
| Failure | What to build |
|---|---|
| The user does not exist | Search for the user first and check the list is not empty. A read by id fails; a search returns nothing. |
| A connection has expired | No retry fixes this. Record it, alert a person, and stop cleanly so the run can be repeated. |
| The other system is slow | Retry with a delay, then record the item as pending so the next run picks it up. |
| Half the work was done | Record each change as it is made, so a repeat run skips what already happened. |
5. Record every failure
Build one helper flow that writes a row to an error table: when, which flow, which card, the error text, the id of the item being processed and the run’s id. Call it from every If Error branch. Keep the error text short enough for a table cell. Then add one scheduled flow that counts new rows and tells someone. A failure that reaches a table and a person is an incident; one that reaches neither is a breach waiting to be found.
One row, as an example. With this row the morning check can say which person still has which group, and which run to open.
| When | Flow | Card | Error | Item | Run |
|---|---|---|---|---|---|
| 2026-11-10 02:14 UTC | Leaver: remove access | Remove User from Group | 429 Too Many Requests, after 3 attempts | user 00u1ab2cd3, group 00g9xy8zw7 | the run’s execution id |
6. Events that never arrive
Flows started by an Okta event depend on that event being delivered. Delivery is not guaranteed. For anything that removes access, add a scheduled flow that reads the System Log for the same event type since its last run and processes anything the event flow missed.
7. Check the flows you already have
If you have more than a handful of flows, you will not find the unguarded cards by opening each one. Export the folder from the Workflows console and assess it with Assay: it lists every card that changes something with no error handling, every loop with no decision about a failed item, and every flow that fails without telling anyone. It reads the export file; it needs no credentials and connects to no tenant.
Sources
- Okta Workflows documentation: error handling on cards, the If Error card and helper flows, and Workflows system limits.
- Okta developer documentation: rate limits and the rate-limit response headers.
- Okta developer documentation: the System Log API and event hooks.
Okta and Okta Workflows are trademarks of Okta, Inc. Assay is independent of Okta. Card names and limits change: check Okta’s current documentation and test in a preview org before you rely on any pattern here.