Error handling patterns for business workflows determine whether a failure becomes a small correction or a lost customer commitment. Workflow software coordinates durable records, people and external systems over time. A payment may succeed while a notification fails; an approval may expire while its page remains open; a partner timeout may hide a completed booking. Treating every condition as a red banner or thrown exception loses the business state needed for safe recovery.
Good error design answers four questions: what outcome was attempted, what is now known to have happened, who can act next and how the system will prevent duplicate or unauthorized effects. It separates user-correctable input, business-rule rejection, conflict, transient dependency failure, uncertain outcome and internal defect. This guide complements incident response for web platforms by focusing on product-level failure and recovery.
Classify failure by recovery responsibility
Validation means the submitted data does not satisfy a documented format or rule and the user can correct it. Business rejection means the request is understood but not allowed in the current state, such as approving an already closed order. Conflict means state changed since the user's view. Transient failure may succeed later without changed intent. Uncertain outcome means a remote effect may have happened despite a timeout. Internal defect means expected software behavior failed and requires investigation. These categories drive different messages, retry and ownership.

Create stable machine-readable problem types and map them to business semantics. RFC 9457 defines application/problem+json with type, title, status, detail and instance. Use a resolvable type for documented API problems and an opaque occurrence reference for support correlation. Do not expose stack traces, database errors or secret values. The HTTP status and problem body should agree, while clients use structured extensions instead of parsing prose.
| Failure class | User or client response | System responsibility |
|---|---|---|
| Validation | Correct identified fields | Preserve input and describe constraints |
| Business rejection | Choose an allowed alternative | Explain current state and policy |
| Conflict | Refresh and review changed record | Return current version and protect updates |
| Transient dependency | Wait or allow bounded retry | Backoff, preserve intent and show status |
| Uncertain external outcome | Do not repeat blindly | Reconcile using identifiers and evidence |
| Internal defect | Use safe reference and alternate path | Contain, alert and investigate |
Preserve durable state around partial failure
Define transaction boundaries around local invariants, not an imagined distributed transaction. Commit a durable intent before asynchronous external work when the business needs follow-up. Use an outbox to publish events consistently with state. Represent pending, completed, failed and awaiting-correction states explicitly. Users should be able to leave and return without losing the operation. Avoid a spinner whose browser process is the only record that work is happening.
For multi-step workflows, record completed steps and compensation rules. Compensation is a business action, not database rollback: cancelling a booking, issuing a credit or restoring inventory may require authority and may incur cost. Identify irreversible points and require confirmation before crossing them. If a later step fails, present the actual partial state and next owner. Do not claim the entire workflow failed when a consequential first step succeeded.
Write accessible messages that support correction
A useful message states what could not be completed, identifies the affected field or object, explains what the person can do and preserves a support reference when needed. WCAG 2.2 requires detected input errors to be identified and described in text, with correction suggestions when known and safe. Associate messages programmatically with controls, move focus appropriately, preserve valid entries and avoid relying on color. Do not blame the user or use internal codes as the only explanation.
Keep security boundaries. A sign-in error should not reveal whether a private account exists; an authorization denial should not expose another tenant's record; a failed payment message should not display sensitive processor detail. Provide enough guidance for legitimate recovery through an approved route. Use consistent wording for stable problem types, localize human text and keep machine behavior independent of translated detail.
| Weak message | Better message | Recovery behavior |
|---|---|---|
| Something went wrong | The booking was not sent; no carrier reference was created | Retry is safe with same request ID |
| Invalid input | Pickup date must be today or later | Focus field and preserve other values |
| Request failed | This record changed since you opened it | Show changes and require review |
| Gateway timeout | We are checking whether the payment completed | Disable duplicate submit and reconcile |
| Forbidden | You do not have permission to export this account | Provide access request route without leaking data |
Retry only when repetition is safe
Automatic retry is appropriate for classified transient failure when the operation is idempotent or protected by an idempotency key. Use bounded exponential delay with jitter and a total age limit. Honor dependency guidance. Stop on validation, authorization and business rejection. Preserve the original authority and check that it remains valid before a delayed action. A retry must not extend a user's permission indefinitely.
When a timeout leaves outcome uncertain, reconcile before repeating. Query the provider with a stable business or idempotency identifier, inspect webhook evidence or route the case to an operator. Store attempt and response metadata without sensitive payloads. Make duplicate suppression visible in audit. Exactly-once delivery cannot be assumed across networks; the business effect must be protected at the receiving boundary.
Give operators evidence, not noise
Correlate the user action, workflow, job and dependency calls with a stable reference. Record state transitions, problem type, attempt, dependency, policy decision and result. OpenTelemetry defines conventions for exceptions in spans, but exceptions should be recorded when they represent meaningful errors for the operation, not as a substitute for modeled business rejection. Metrics should show failure and recovery by workflow step, tenant or dependency without exposing sensitive dimensions.
An operator view should display current business state, completed effects, pending work, recent failures, next retry and authorized actions. Provide runbooks for replay, correction, cancellation and escalation. Recovery actions should be idempotent and audited. Alert on customer-impacting state, growing age, repeated uncertain outcomes and broken reconciliation, rather than on every handled exception. OWASP guidance reinforces using generic external responses while retaining protected internal diagnostic detail.
Test failure paths as first-class behavior
Write tests for each taxonomy class and durable boundary. Inject dependency timeouts before and after external success, duplicate callbacks, stale versions, authorization changes, process crashes and partial batch failure. Verify state, message, audit and recovery, not only status code. Use controlled clocks for expiry and retry. Accessibility tests should trigger all validation errors and confirm focus, association and preserved input.
Review production failures to improve domain types and controls. Do not create a new public problem type for every stack exception; stabilize types that clients and users can act on. Track unknown-error rate, repeat failure, recovery time, duplicate prevention and abandoned workflows. A decline in raw exceptions is not success if customers remain stuck in pending states.
Govern the error contract across clients and services
Maintain a catalog of public problem types with owner, meaning, status, safe extensions, retry semantics and client guidance. Review additions for overlap and security. Version behavior compatibly: clients should tolerate new extension members, while the server should not silently change a type from correctable to irreversible. Mobile and partner clients may remain deployed longer than the web interface, so coordinate deprecation and keep type documentation stable.
Separate external contract from internal diagnosis. The client receives a stable problem and occurrence reference; protected telemetry records service, code location, dependency, trace and sanitized context. Support tools can resolve the reference under authorization. This structure lets engineers investigate without exposing stack details to users or forcing support staff to search unrelated logs. Retention should reflect operational and privacy requirements.
Require recovery evidence before release
For each consequential workflow, acceptance should include validation, stale-state conflict, authorization denial, dependency timeout before and after remote effect, duplicate submission, process restart and operator recovery. Verify the visible message, durable state, audit event, telemetry and next action. Confirm that keyboard and assistive-technology users can find and correct errors. Test that sensitive data and object existence are not leaked.
After release, review abandoned and repeatedly retried workflows, not only server error rate. Sample occurrence references through the support path and check that records are sufficient. Track time from failure to correct ownership and verified recovery. Feed recurring failure into product design, dependency contracts and runbooks. The best error program gradually removes ambiguous states while making unavoidable failure easier to understand.
Key takeaways
- Classify failures by recovery responsibility and business meaning.
- Represent partial and pending work in durable state.
- Give users specific accessible correction guidance without leaking internals.
- Use bounded retry only with idempotency and current authority.
- Equip operators to reconcile, recover and audit uncertain outcomes.
Frequently asked questions
Should every error have a code?
Stable actionable problem types should. Internal defects also need a unique occurrence reference, but not a public contract for every exception. Separate the semantic problem type from the specific incident identifier.
Are toast notifications enough?
Usually not for consequential or field-specific failure. Toasts can disappear, be missed by assistive technology and lose context. Place correction near the affected control or workflow state, manage focus and keep the status available until resolved.
Should users be given a Retry button?
Only when the system knows repetition is safe. For uncertain external outcomes, show that reconciliation is underway or route to support. Use the same idempotency key and prevent repeated clicks from creating parallel effects.
Conclusion
Professional error handling preserves truth and guides the next safe action. It models business failure, commits durable intent, protects repetition, explains correction and gives operators evidence for recovery. That design turns failure from an accidental edge case into an understandable part of the workflow.
Choose one workflow with an external dependency and test every failure boundary. Document what the user sees, what state is durable and who acts next. The ambiguities uncovered there are the highest-value improvements to make.