Flowable error handling with BPMN errors and retries

Flowable separates business errors that a model should route from technical failures that the async executor retries or parks in a deadletter job. This page explains how to design, configure and operate both.

On this page

Flowable error handling: when a failure is a business path and when it is a retry

A service task calls a payment, credit bureau or ERP API and the call fails. Before writing any error handling, decide who owns the next step: the process model, or the engine's job retry mechanism. Flowable separates these two paths. A BPMN error thrown from a Java delegate is a business outcome that the model can catch with an error boundary event or an error start event, and the model then routes the instance to a defined branch such as manual review or a rejection letter. A technical exception, such as a dropped connection or a timeout, is not a business outcome. For those, Flowable relies on asynchronous job retries and, once retries are exhausted, a deadletter job that an operator must handle. Classify each external failure mode against this split before the model is drawn, because a failure that is caught as a BPMN error will not be retried by the engine.

The practical check is to list every failure a service task can return and label each one as either a condition the business process should handle (a declined account, a missing customer record, a rule violation) or an infrastructure fault that should be retried or escalated to operations. Map the first group to BPMN errors with explicit error codes, and confirm in the Flowable documentation how error codes are matched to boundary events and how unmatched errors propagate. Map the second group to the async job configuration, including the retry count and interval set through the failedJobRetryTimeCycle setting. Verify both paths in your own environment, because retry behavior depends on which transaction boundaries and async continuations the model contains, and on the database and deployment you run.

This page reflects open-source Flowable engine guidance drawn from the Flowable documentation. It is not a substitute for testing in your environment. Retry timing, deadletter handling, transaction scope and the interaction with your external systems should be exercised with failure injection against the database, network and version you plan to run. The Flowable advanced documentation describes the async executor that drives these jobs, and it is the natural next read before you commit to a retry design.

Which error mechanism fits which failure

This table compares four Flowable failure paths: a BPMN error thrown from a Java delegate, an error boundary event on an activity, an error start event or event subprocess, and async job retries that end in a deadletter job. Read each row to see what triggers the path, where it is configured, what state survives, and when it fits the failure you are designing for.

MechanismTrigger and configurationWhat it keeps or rolls backWhen it fits
BPMN error from a Java delegateDelegate throws a BPMNError with an error code; the engine matches the code to an error event in the modelWork done before the throw in the same transaction is rolled back to the activity boundary; the error routes the token onwardBusiness failures the model should handle, such as a rejected validation or a declined external decision
Error boundary event on an activityAttached to a task or subprocess and matched by errorRef or error code; interrupts the activity when it firesThe activity's own changes are discarded; variables set before the error may depend on transaction and scope settings you confirmKnown failures inside one step where the process should take a specific alternative path
Error start event or event subprocessError start event in an event subprocess, scoped to the parent process or subprocess; matches errors that no closer handler caughtInterrupts the enclosing scope and runs the handler flow; the scope state is replaced by the handler pathCross-cutting failures that need one recovery or notification path for many activities
Async retries with failedJobRetryTimeCycleJob fails with a technical exception; retry cycle configured in the engine configuration or on the async job, not as a BPMN errorThe failed job's transaction is rolled back and the job is retried later; process state before the async boundary is keptTransient technical faults such as a temporarily unavailable service, where the same call may succeed later
Deadletter jobRetries are exhausted or the cycle is not configured to retry further, leaving the job in the deadletter tableThe job stays in the database for inspection; the process is paused at the failed step until an operator actsFailures that need human or tooling review, such as bad data or a defect; confirm how your operations team retries or deletes these jobs
Choosing between retries and BPMN errorsA technical exception versus an error code thrown on purpose; the distinction is set in the delegate codeRetries re-run work; a BPMN error routes the flow, so idempotency of external calls matters in both casesUse retries for faults that may clear on their own and BPMN errors for outcomes the business process must model

Deciding between a business path and a technical retry

A business rejection is an expected outcome, such as a credit application declined by a rule or an order refused by a stock check. It belongs in the process model, not in the failure machinery. The usual pattern is an exclusive gateway on a decision variable, or a BPMN error thrown from a Java delegate and caught by an error boundary event that leads to a rejection path. Either way, the instance completes along a visible branch and nothing looks broken to the operator.

  • Model the outcome explicitly: Give the rejection its own sequence flow and end event so the process owner can read it in the diagram.
  • Use a BPMN error when code decides: Throw a BPMN error with a clear error code from the delegate and match that code on an error boundary event.
  • Keep retries out of it: A rejection is an outcome, so no asynchronous job should retry it.
  • Store the reason: Save the rejection reason in a process variable so it appears in the instance history.

Check that each rejection path shows up in the process history with its reason, and that no failed jobs are created for it.

Configuring error handling in a Flowable project

This sequence takes a Flowable project from undefined failures to a tested setup where business errors route to defined paths, technical failures retry on a set schedule, and exhausted jobs land in the deadletter queue for review. Each step ends with a check you can run before moving on.

  1. Define the error codes in the model

    The process architect and a developer agree on a short catalog of error codes, such as one code per business rejection the process must handle, and record each code with its meaning and owner. In the BPMN model, each code is set as the errorCode on the error definitions that boundary events and error end events will reference. Done means every code in the catalog appears once in the model and nowhere else under a different spelling. Check this by searching the deployed BPMN XML for each code and confirming it matches the catalog exactly.

  2. Throw BPMN errors from the delegate

    A Java developer implements the JavaDelegate or service task logic so that expected business failures throw a BpmnError carrying one of the cataloged codes, with the reason captured in the exception message for the audit trail. Technical failures, such as a timed-out call to an external system, should not be disguised as business errors, so they are left to propagate as ordinary exceptions and are handled by the job retry mechanism. Done means a unit test invokes the delegate with a failing input and asserts the exact error code. Check this by confirming the test fails if the code string is changed.

  3. Add error boundary events and handling paths

    The process developer attaches an error boundary event to each service task that can throw a cataloged error, sets the boundary event's error reference to the matching code, and models the recovery path, such as a manual review task or a compensating step. Where the process should stop on a failure it cannot handle, an error end event or an error event subprocess is used so the outcome is explicit in the diagram. Done means the process diagram shows a handling path for every business error. Check this by running a test that triggers each error code and confirming the token follows the boundary path rather than failing the instance.

  4. Confirm unmatched errors surface as technical failures

    The developer runs a test that throws an error code absent from every boundary event on the activity, which is the case that most often hides defects. In a correctly built model, the engine does not find a matching handler, so the failure is not silently absorbed and surfaces as a technical exception on the job. Done means the test shows the job or instance in a failed state with the unmatched code recorded in the logs. Check this by confirming the failure is visible through the engine's job queries and that no path quietly continues past the step.

  5. Set failedJobRetryTimeCycle on async activities

    The platform or developer team sets the failedJobRetryTimeCycle configuration on the async activities whose technical failures should be retried, such as calls to external services, and chooses the cycle to match how long the downstream system is typically unavailable. Confirm the exact configuration key and syntax against the Flowable configuration and advanced documentation for the version in use, because the setting can be applied at the engine level or on individual activities. Done means the configuration is in version control and the effective value is visible in the running engine's configuration. Check this by forcing a technical failure and observing that the job is rescheduled according to the cycle.

  6. Test retry and deadletter behavior with job APIs

    The QA engineer forces a technical failure that persists across all retries, then uses the Flowable management job APIs to list failed jobs, inspect exception messages, and confirm the job moves to deadletter once retries are exhausted. The test also covers a job that succeeds on a later retry, so the team sees both outcomes. Done means the test suite contains one case for retry success, one for deadletter, and one for a business error that bypasses retries. Check this by confirming a deadletter job can be retried manually through the API and completes once the underlying fault is removed.

  7. Document the operational runbook

    The engineering lead writes a short runbook that lists each error code, its handling path, which failures are retried automatically, and who reviews deadletter jobs and on what schedule the team agrees. The runbook names the job API calls and the monitoring signals operations staff will use to find failed work. Done means the runbook is reviewed by the process owner and operations team, and any unresolved questions about retry thresholds or ownership are recorded as open items. Check this by having an operator who did not build the process locate and retry one test deadletter job using only the runbook.

Operational risks of retries and deadletter jobs

Retries and deadletter jobs keep a process moving after a failure, but they also repeat work and hold data that someone must own. The cards below cover the risks that appear once a Flowable engine is running in production and the checks that keep them visible to operators and auditors.

Duplicate side effects on retry

A failed async job is retried by the engine, so a service task that already charged a card, sent a notice or created a record may run again. Flowable does not know what the external system did before the failure. Before you set failedJobRetryTimeCycle, confirm that each delegate calling an external system sends an idempotency key or checks state first. Review which steps are safe to repeat and which must be split into a separate transaction boundary.

Sensitive data in exception messages

Exception text thrown from a delegate can end up in job exception stack traces, logs and the job tables the monitoring APIs expose. Account numbers, personal identifiers or tokens placed in a message are then readable by anyone with operator access. Define a rule for what error messages may contain, keep payloads out of exception text, and test a failing job to see what the stored stack trace actually shows.

Deadletter jobs without an owner

When retries are exhausted, the job moves to the deadletter state and waits there. If no team is assigned to watch the deadletter queue, failed work accumulates silently and customers only learn of it later. Assign an owner for each process family, set an alert on deadletter counts through your monitoring stack, and agree on who can reprocess, cancel or compensate each job.

Retry settings that differ across environments

Retry counts and intervals set in one environment may not match production, so failures in staging appear to recover while production jobs fail faster or slower than expected. Keep the retry configuration in version control with the process deployment, and record the effective value per environment. Confirm the configuration key and its behavior against the Flowable version you run.

Manual reprocessing that changes data

Operators who move a deadletter job back to the executable state or retry it by hand can repeat the same side effects, or run a step after its data has changed. Limit who can reprocess, require a reason in the change record, and check the process variables before the job runs again. Route these actions through your access model rather than shared administrator accounts.

Audit evidence for failure handling

Auditors ask who saw a failure, when it was handled and what changed as a result. Flowable history and job records show parts of this trail, but retention settings, log shipping and ticket references must be designed to link them. Agree on the evidence each failure type should produce, check that history retention covers the review period, and test that you can reconstruct one failed instance end to end.

Questions teams ask before building Flowable error handling

What drives the cost of Flowable error handling work?

The main drivers are the number of external integrations that can fail, the number of distinct error paths the process needs, and how much automated test coverage already exists for the process. Each external system adds its own failure modes, error codes and retry rules that have to be agreed with the system owner. Each error path needs a designed outcome, such as compensation, a manual task, a retry or a deadletter job, plus a test that proves it. Weak existing coverage raises cost because the partner or your team has to build a safety net before changing error behavior. Ask any partner to quote these three inputs separately so you can see which one moves the number.

How long does this work usually take?

Duration depends on the same drivers as cost: the count of integrations, the count of error paths, and the state of existing tests and deployment pipelines. A process with a few integrations and tests already in place is a different project from a long-running process spanning several systems with no test suite. No duration figure is given here because none was supplied for this topic. Ask the partner to break the timeline into discovery of the error catalog, build of delegates and boundary events, test automation, and a staged rollout to production, and to state which of those depend on your team's availability.

What does our team need to provide before the work starts?

Three inputs carry most of the weight: process owners who can decide what a business failure means, a written error catalog that lists each failure, its source system and the expected outcome, and operator runbooks that describe who acts when a job lands in deadletter. Without named process owners, error paths get defined by engineers alone and often mismatch business policy. Without an error catalog, the team cannot test the paths that matter. Without runbooks, retries and deadletter jobs have no one to handle them after go-live. Confirm access to the target environments, the test systems behind each integration, and a named contact for each external dependency.

Can our internal team build Flowable error handling without a partner?

Yes, a team with Java and Flowable experience can build this internally, and the Flowable documentation covers BPMN errors, error boundary events, the async executor and retry configuration. The questions to answer honestly are whether engineers have built and operated Flowable processes in production, whether they have capacity for test automation and runbook writing, and whether the process owners will commit time to the error catalog. A partner is most useful when the team lacks production experience, when several systems must be wired together under deadline, or when an audit requires documented evidence. Compare both options against the same written scope.

What goes wrong most often with BPMN errors and retries?

Teams most often throw generic exceptions where a BPMN error with a meaningful code was intended, so error boundary events never catch them. Retry settings are often left at defaults, which either retry a permanent business failure forever or give up on a temporary outage too quickly. Some teams rely on transactions that roll back the work a delegate already completed, leaving external systems and process variables out of step. Deadletter jobs pile up because nobody owns them. Each of these is checked in the design review and in tests that trigger each error path on purpose.

Does DeployOpen provide compliance certification for our error handling?

No. DeployOpen prepares systems, controls and evidence for compliance assessments, such as documented error handling, retry behavior and job history. Independent auditors and certification bodies issue any audit report, attestation or certification. Ask the partner to show which evidence they will produce, including test results, configuration records and runbooks, and ask your auditor which of those artifacts they expect to see.

How should we ask a partner to quote this work?

Ask for a fixed-scope proposal that lists the processes in scope, each external integration, the error catalog entries to be covered, the retry and deadletter policy, the test suite expected, the handover and runbook deliverables, and your responsibilities. DeployOpen agrees scope, deliverables and timelines in writing before work starts and does not publish a price list, so the quote follows from that written scope. Ask any vendor to separate one-time build effort from recurring support, upgrades and continuous testing, which are available as retainers.

How DeployOpen scopes and delivers Flowable error handling

DeployOpen treats Flowable error handling as a written scope covering which failures end in a BPMN error, which retry on the async executor and which reach a deadletter job. The work runs inside your own infrastructure, with Flowable engines, delegates and runbooks handed over to your team.

How we scope

  • Inventory every service task and delegate that calls an external system, and classify each failure as business, technical or transient.
  • Agree in writing which failures map to BPMN error events, which retry with failedJobRetryTimeCycle and which end in a deadletter job.
  • Record the owner who decides on deadletter jobs, and the operational signal that tells them a job needs attention.

How we build

  • Implement Java delegates that throw BPMN errors with stable error codes, and map exceptions from client libraries to those codes.
  • Model error boundary and error start events on the process definitions, and confirm where each error is caught and where the transaction rolls back.
  • Test retry timing, lock expiry and deadletter behavior against a database that matches your production type, using your deployment pipeline.

How we hand over

  • Deliver the error code catalog, the retry configuration and the process model documentation as versioned artifacts in your repository.
  • Provide a runbook for inspecting failed and deadletter jobs, including who may retry or delete them and how to record the action.
  • Walk your operations and development staff through the test cases so they can extend error handling for new services.

How we support

  • Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
  • Ongoing support, Flowable upgrades and continuous testing are available as retainers once the handover is complete.
  • Confirm with your team which Flowable version and database you run, since retry and lock behavior should be verified against that setup.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.