Which Flowable gateway fits each routing decision
Pick the gateway by answering two questions: how many outgoing paths should run for a given token, and how must a join wait for incoming paths before it lets the process continue. An exclusive gateway sends the token down exactly one path, chosen by conditions evaluated in order. A parallel gateway forks a token into every outgoing path and, as a join, waits until all incoming paths have arrived. An inclusive gateway fires every path whose condition is true, so it fits routing where one, several or all paths may apply. The Flowable BPMN engine, licensed under Apache 2.0 and exposed through Java and REST APIs, evaluates these rules at runtime, so a wrong choice shows up as a token that stops or takes an unexpected branch rather than as a deployment error.
Most stalled tokens trace back to a mismatch between the split and the join. A parallel fork closed by an exclusive merge, or an inclusive split whose join expects a fixed number of arrivals, can leave a token waiting forever because the join never receives the tokens it counts on. Before build starts, write down for each split which paths can be active together, what each condition reads from process variables, and which join must match it. Event-based gateways, which wait for the first of several events, follow the same logic and need their own plan for timeouts and unused branches. Confirm the expected behavior of each gateway type against the Flowable BPMN documentation for your version, because gateway semantics and default-flow handling are the points teams most often get wrong.
Treat gateway logic as a design artifact that the process team reviews before the model is built. Ask the team to list every split with its conditions, default flow, variables used and expected path count, and every join with the number of incoming tokens it must wait for. Then test each path with variable combinations that include missing or null values, because a condition that throws or never matches is a common cause of waiting tokens. Flowable's own process documentation, linked from the pillar resources, is the next read for the element-level details, and your team should confirm which behaviors apply to the engine version you intend to deploy.
Gateway options and when each one fits
This table compares exclusive, parallel, inclusive and event-based gateways on routing, join handling, condition requirements and typical use. Read each row as a question you should answer for your model before you choose a gateway type. Confirm engine-specific details, such as default flow handling and join semantics, against the Flowable version you run.
| Gateway | Routing behavior | How the join is handled | Condition requirements | Typical use |
|---|---|---|---|---|
| Exclusive (XOR) | One outgoing flow is taken, the first whose condition evaluates true in document order, or the default flow | Passes through each incoming token without waiting for others | Each non-default outgoing flow needs a condition; the default flow should not | Single decision point, such as approve or reject routing |
| Exclusive as a join | Not applicable as a fork | Each arriving token continues on its own, so a single token can run twice if several reach it | Not applicable | Merging alternative branches that never run together |
| Parallel (AND) | All outgoing flows are taken, creating one token per flow | Waits until a token has arrived on every incoming flow before continuing | Outgoing flows carry no conditions; conditions are ignored | Running independent work streams at the same time |
| Inclusive (OR) | Every outgoing flow whose condition is true is taken, plus the default flow if no condition matches | Waits for tokens on the incoming flows that were activated at the fork, which depends on the fork's evaluation | Each non-default outgoing flow needs a condition; a default flow is recommended | Optional parallel paths where any subset may apply |
| Event-based | Waits and takes the path of the first catch event or receive task that fires | Not a join; the first event wins and the other paths are cancelled | Targets must be catch events or receive tasks, with no conditions on the flows | Waiting for whichever happens first, such as a reply or a timeout |
| Missing default flow | Exclusive or inclusive gateway with no true condition ends the path with an error or stalls the token | Token may wait with no outgoing path taken | Confirm the engine's exact failure behavior for your version | Add a default flow on every branching gateway that can see unexpected data |
| Condition evaluation | Conditions read process variables; errors or null values can change which path runs | Not applicable | Use expressions that handle missing variables and check types | Keep conditions simple and test each branch |
| Modeling mismatch | A parallel fork closed by an exclusive join, or the reverse, leaves tokens waiting or duplicates work | Mismatched join types are the most common cause of stuck instances | Check that each fork has a matching join type | Review every fork and join pair in the diagram |
Modeling mistakes that leave tokens waiting
Most stuck Flowable instances trace back to a gateway that does not match how the process actually branches. The cards below cover six mistakes that look valid in the modeler but leave tokens waiting at runtime, with the symptom an operator sees and the model change that resolves each one.
Parallel fork joined by an exclusive gateway
A parallel gateway splits the token into several paths, but an exclusive gateway is used to merge them. The exclusive join passes the first arriving token and ignores the rest, so the process can move on before every branch has finished, or the other branches stay active with no later step to consume them. The symptom is a process that continues too early or leaves orphaned executions in the runtime tables. The fix is to join parallel forks with a parallel gateway, so the engine waits for one token per incoming path. Check every merge point against the gateway type that created the split.
Inclusive join with no matching condition
An inclusive gateway joins only the paths that were activated by its matching inclusive split. If a split activates two paths but the join expects three incoming tokens, or a path that was never activated still points into the join, the token waits for an arrival that never comes. The operator sees an instance parked at the join with no active work upstream. Make sure each inclusive join has a clear counterpart split, and that the conditions on the split are designed so at least one flow is always taken. Test the cases where only one condition is true.
Missing default flow on an exclusive split
An exclusive gateway takes the first outgoing flow whose condition evaluates to true. If none evaluates to true and no default flow is set, the engine has no path to follow and the instance fails at that gateway. The symptom is an exception in the job or request log and an instance stopped at the split, often only for edge-case data. Set a default flow on every exclusive split that represents the fallback branch, such as a manual review or rejection path. Then test a record that matches no explicit condition to confirm the fallback is reached.
Event-based gateway with no event source
An event-based gateway waits for one of several catch events, such as a message, signal or timer, to fire. If no deployed process, API call, or timer can trigger any of the target events, the instance waits indefinitely. Operators often see an active event subscription with no matching message in the integration logs. Confirm that each catch event has a defined source, whether a correlated message sent through the API, a signal, or a timer with a valid duration. Add a timer branch as a bounded fallback where the business needs an escalation rather than an open wait.
Condition that reads an unset variable
A sequence flow condition that refers to a process variable which has not been set yet either fails to evaluate or evaluates against a missing value, depending on the expression and the path taken. Variables set by a task that was skipped, or by a called process that returned early, are common causes. The symptom is an expression error or an unexpected branch choice that appears only on some instances. Initialize required variables at process start, or guard the expression with an existence check. Review which upstream activities can be bypassed before each condition that depends on their output.
Loop without an exit path
A gateway that routes a token back to an earlier activity, such as a rework or resubmit step, needs a condition that eventually sends the token forward. If the loop condition never becomes false, for example because the reviewer decision is never recorded or the counter is not incremented, the token cycles indefinitely and the instance accumulates work without completing. Operators see repeated task creation or jobs that keep firing on the same activity. Add a loop counter, a maximum retry count routed to an escalation path, or a timer boundary event that exits the loop after a defined period.
How conditions and default flows decide the path
An exclusive gateway sends each token down exactly one outgoing sequence flow. The engine evaluates the conditions on the outgoing flows in the order they are defined in the model, takes the first flow whose condition evaluates to true, and ignores the rest. That single-path rule is what makes the gateway exclusive, so the order of outgoing flows matters when more than one condition could be true. Overlapping conditions are a design defect, not a runtime decision the engine will resolve for you. In the design review, check that the conditions are mutually exclusive by intent and that each one reads only variables the process has already set. Flowable evaluates these conditions as expressions over process variables, so a misspelled or uninitialized variable is a common cause of unexpected routing. Confirm the expression syntax, the variable scope rules and how null values behave in the Flowable docs for your version, and make variable naming and initialization part of the review checklist.
A default flow is the designated outgoing sequence flow that the exclusive gateway takes when no other condition evaluates to true. Without one, a token that matches no condition has nowhere to go, and the instance stops at the gateway with an error instead of advancing. Mark exactly one outgoing flow as the default and make it the path for the unexpected case, such as a manual review or an error handling branch, rather than a copy of the happy path. Be deliberate about where the default leads, because it hides modeling gaps: a default that quietly routes everything into a generic task can mask a missing business rule for months. Inclusive gateways behave differently. Every outgoing flow whose condition is true fires, so one token can split into several tokens, and if no condition is true the default flow is taken. The matching join must then wait for exactly the tokens that were activated, which depends on the gateway pair the model uses. Confirm in the Flowable docs how inclusive joins determine the activated paths before you rely on them in production.
How to set up and test gateway logic in a Flowable model
This sequence turns routing intent into a deployed Flowable model whose every branch, default path and join has been exercised in a test environment. Each step ends with a runtime check so gaps show up before production.
Agree routing rules with the process owner
Before any modeling, the process owner and the engineering lead write down each decision point as a plain-language rule: which outcomes exist, which variable drives each one, and what happens when no rule matches. Include which branches must run in parallel and where separate paths must rejoin. Store this as a short decision table or numbered list in your project repository, approved by the owner. You are done when every gateway in the planned diagram maps to one written rule and one named owner has signed off on the list.
Model each gateway with explicit conditions
Place the gateways in the BPMN model using the agreed rules. Give every exclusive or inclusive gateway a condition on each outgoing sequence flow, a default flow for the fallback case, and a clear name on each flow. Use parallel gateways only for forks and joins that always run together, and check that every fork has a matching join. Open the model in your modeling tool and confirm that no outgoing flow lacks a condition except the designated default. You are done when the diagram validates and a reviewer can read each routing decision from the labels alone.
Set the variables conditions read at start
List every process variable that a condition or expression reads, then make sure the start call or the preceding task always sets it. Decide on types up front, since a comparison against a missing or differently typed value can fail or silently take the wrong branch. Where a value may be absent, add a guard in the expression or set a default at the start of the process. You are done when a written list of required variables matches the start API contract and the variables appear in the runtime data after a test start.
Deploy the model to a test environment
Deploy the BPMN file to a test Flowable engine that uses the same engine version, database type and configuration as production. Confirm the deployment appears in the repository tables and that the process definition key and version are the ones you expect. Record the deployment identifier so that test results can be traced to the exact model. Confirm whether the test database matches production in the database type and the MySQL precision settings noted in the configuration documentation if you use MySQL. You are done when the definition is listed as deployed and a test start creates a process instance.
Verify active tasks and waiting executions
After each test start, query the runtime data for the active activities and executions. Check that exactly one outgoing path is taken at each exclusive gateway, that the expected number of branches is active after each parallel fork, and that inclusive gateways activate only the branches whose conditions are true. Use the history or runtime APIs to compare the actual activity sequence with the expected one written in step one. You are done when each sample instance shows the expected active tasks and no execution is stuck at a join.
Run path tests for every branch
Write one automated test per branch of every gateway, including the default flow, and drive each with input variables chosen to force that outcome. Add a test for the empty-join case, where a parallel or inclusive join waits for a branch that never activates, and confirm the instance is flagged rather than left waiting without explanation. Include a test where a condition variable is missing to confirm the behavior you designed in step three. Run the suite against the test deployment and record the results. You are done when every branch, the default and the empty-join scenario have a passing test tied to the deployment identifier.
Review waiting tokens before promotion
Before promoting the model, query the runtime for process instances that are still open after every test has completed and classify each one. Any instance waiting at a join, an event gateway or an unexpected task indicates a modeling or variable problem to fix before release. Document the findings, the fixes and the final test run in your change record so the model can be audited later. You are done when no test instance is unexplained, the change record links the deployment to its passing tests, and the owner has approved promotion.
Gateway choices by process situation
When a process must send work down exactly one path, such as approve, reject or request more information, an exclusive gateway is usually the starting point. Each outgoing sequence flow carries a condition, and the engine takes the first one that evaluates to true in document order, so the conditions should be mutually exclusive and a default flow should cover the remainder. Overlapping conditions are the most common source of surprise here, because the result depends on how the model was ordered.
- Make conditions mutually exclusive: Write each condition so that only one can be true for any valid input, and document the variable each one reads.
- Always set a default flow: Route unmatched values to a visible path, such as a manual review task, instead of letting the process fail at runtime.
- Check the variable exists: Confirm that the variable is set on every route that reaches the gateway, including routes that skip earlier tasks.
- Avoid joins on exclusive gateways for parallel work: A merge of exclusive paths passes each arriving token straight through, so it does not wait for other branches.
Before release, test every outcome with a process instance that exercises each condition and the default flow, and confirm that each instance reaches an end event with no active jobs or tasks left behind.",
When several pieces of work must all run and the process should continue only after every one of them is done, use a parallel gateway to fork and a matching parallel gateway to join. The fork creates one token per outgoing flow, and the join waits until a token has arrived on every incoming flow before it continues. The main risk is a mismatch between forks and joins, because a join that expects more tokens than a fork produces will hold the process open indefinitely.
- Pair each fork with a join: Use a parallel gateway to join the branches created by a parallel fork, and avoid joining them with an exclusive gateway.
- Watch for conditional flows into a join: A conditional flow that is skipped on some path can leave the join waiting for a token that never arrives.
- Decide on async boundaries: If branches run as asynchronous jobs, confirm that the join still receives every token after retries.
- Keep branch variables separate: Concurrent branches writing the same variable can overwrite each other, so assign distinct names where possible.
Test with a process instance in which every branch completes, then with one in which a branch is skipped by a condition, and confirm that the join either releases or that the model is corrected before deployment.",
When some data-dependent work may or may not be needed, and several of those side tasks can run together, an inclusive gateway fits better than a parallel fork. It evaluates the condition on each outgoing flow and creates a token for every flow that is true, so zero, one or several branches can run. The join must know how many tokens to expect, which is the part teams most often get wrong. Inclusive joins are sensitive to the way the engine counts active paths, so the join design should be checked against each combination of conditions.
- Start with an inclusive fork: Use it when the set of active branches depends on data and any combination is valid.
- Plan the join deliberately: Confirm how the engine decides the join is complete, and test the case where no condition is true.
- Provide a fallback when none match: Add a default flow or an explicit check so that a process instance cannot finish with no active path.
- Document the condition set: Keep the conditions in one place that reviewers can read, because they define which work is performed.
Test every meaningful combination of the condition variables, including the case where all conditions are false and the case where all are true, and confirm that each run reaches its end event cleanly.",
When a process waits for whichever external event happens first, such as a customer reply, a payment confirmation or a timeout, an event-based gateway is the right starting point. The gateway activates a set of catch events, and the first one that occurs determines the path, while the others are discarded. Because only the first event counts, the remaining catch events must be designed so that a late arrival does not create a second path through the process.
- Use catch events behind the gateway: Each outgoing path should begin with an intermediate catch event, such as a message, signal or timer, rather than an activity.
- Account for the timeout path: Pair an event such as a timer with a path that handles silence, so the process cannot wait forever.
- Plan correlation keys: Confirm how the incoming message is matched to the right process instance, since a mismatch leaves the instance waiting.
- Confirm late events are ignored: Check how the engine treats an event that arrives after the path has already been chosen.
Test each path by sending the event alone, by letting the timer expire, and by sending events in the reverse order, then confirm that the unused catch events are cleared and that no instance remains waiting.
What gateway logic changes in operations and audit
A gateway is a routing decision that the engine records, repeats and sometimes leaves unresolved. The cards below connect each modeling choice to a check operations and audit teams can run against live Flowable instances and their history data.
Routing decisions in the audit trail
Each exclusive, parallel or inclusive gateway decision shows up in history only through the activities and sequence flows the engine records, so the reason for a branch lives in your variables and conditions. Check that every routing variable a condition reads is written to history, and that a sample of completed instances can be traced from the start event to the branch taken. Confirm that the condition inputs are retained, not just the outcome.
Running instances after a model change
A running instance keeps the sequence flows of the definition version it started with unless you migrate it, so a routing change may not affect work already in flight. Before deploying a new gateway condition, list the open instances that will reach the changed gateway and decide whether they finish on the old logic or move to the new version. Record that decision as part of the change.
Failed expressions in job and log output
A condition expression that throws an error or references a missing variable fails when the engine evaluates the gateway, not when the model is saved. Check the failed job list and application logs for expression exceptions after each release, and confirm that alerts fire on them. Treat a rise in failed jobs at a gateway as a routing defect, not a transient fault.
Async boundaries and where tokens wait
Where you place asynchronous continuations decides which transaction a token waits in, and a parallel join waits for every incoming token before it continues. Confirm which tasks are marked asynchronous on each side of a fork and join, and check the job table for jobs waiting on a lock that has expired. Tokens stuck at a join often point to a branch that never completed.
Who approves routing rule changes
Routing rules set who gets which work and which controls apply, so their changes carry risk comparable to access changes. Assign approval to the process owner for business intent and to a second reviewer from security or compliance when a condition touches regulated data or approval thresholds. Keep the approval record with the deployment so it can be produced at audit.
What teams ask before building Flowable gateway logic
How much effort does gateway design take compared with the rest of a Flowable model?
Gateway logic is usually a small share of the modeling effort, but it carries a disproportionate share of the testing effort. A gateway is one shape on the diagram, yet each outgoing condition multiplies the paths a case can take. The work that grows with the model is the number of branches, how many variables each condition reads, how often the same decision appears in several processes, and whether conditions should move into DMN decision tables instead of expressions. Ask your delivery partner or internal lead to count the branches and conditions in the first sprint, since that count predicts most of the rest.
Can an internal team build and test Flowable routing on its own?
Yes, if the team has Java or Spring experience and access to the Flowable engine APIs. Gateways themselves are declarative, so the hard part is rarely the BPMN syntax. The harder work is agreeing on the variables and business rules each condition depends on, writing unit tests that drive every path, and proving that a default flow catches cases nobody expected. Teams without a process owner who can state the rules precisely tend to struggle more than teams without Flowable experience. A partner can take the build, the test suite or a review, and the choice depends on where your gaps are.
What should a quote for Flowable gateway work cover?
A quote should follow from a written scope, and it should name each deliverable. Ask for the list of processes or cases in scope, the number of gateways and conditions expected, the decision logic that will live in expressions versus DMN tables, the test cases and test levels included, the review of existing models, and the handover of documentation. Also ask what is one-time work (modeling, tests, initial review) and what recurs (changes after business rule updates, engine upgrades, monitoring). Quotes that do not list the paths to test usually hide the largest variable.
How long do review and testing usually take, and what drives that time?
Duration depends on the number of paths, how clearly the business rules are written down, and how many systems supply the variables that conditions read. Review takes longer when conditions overlap, when inclusive gateways combine several optional branches, or when parallel joins depend on tokens that may never arrive. Testing takes longer when each path needs its own data setup or when a path only fires after a timer or message. No public duration is supplied for this work, so ask for a schedule tied to the scope and to the number of review cycles the business owner can commit to.
What does the business owner need to provide for gateway logic?
The business owner must state each routing rule in plain terms, decide what happens when no rule matches, and approve the test cases that represent real work. Ask them for a sample set of past cases, including unusual ones, and for the rule that applies when two conditions both match. Without these inputs, developers guess at thresholds and defaults, and those guesses become the defects found in production. Plan a named owner with time for review at each milestone.
What does the platform team need to support gateway behavior in production?
The platform team needs a deployment pipeline that versions process definitions, a database the engine can use, and monitoring that shows active instances waiting at joins or inclusive gateways. Flowable's async executor handles timers and asynchronous jobs, and its lock handling lets other nodes pick up work, so the platform team should confirm how the cluster is configured before go-live. Operators also need a way to find tokens stuck at a parallel join. Confirm these items against the Flowable configuration and advanced documentation for the version you run.
Does Flowable gateway work create compliance or security obligations?
Routing decisions often determine who approves work, which records are retained and which controls apply, so the logic is part of the evidence an auditor may examine. Keep the rules, the tests and the version history of each process definition together, so you can show which logic applied to a given case. DeployOpen prepares systems, controls and evidence for compliance assessments, but independent auditors issue any audit report or attestation. Ask your compliance lead which control objectives the routing rules touch before the build starts.
Limits to confirm in your Flowable environment
Gateway behavior depends on the engine version, the Java baseline, the expression language, the database and the way your team inspects running work. Settle these five items before you commit to a model, because each one changes how a gateway condition is written, tested and debugged.
Flowable version and Java baseline
Confirm which Flowable line you will run before you write conditions. Flowable 8.0.0 was released on February 27, 2026, and the 8.0.0 release notes describe a move to Spring Framework 7 and Spring Boot 4 with no backward compatibility for Spring Boot 3. Version 7 moved to Java 17 and Jakarta EE. Check the Java runtime and framework versions your target supports, and the gateway behavior in the release notes for that version, before you copy patterns from older examples.
Expression language and variable scope
Exclusive and inclusive gateway conditions are evaluated as expressions against process variables, so the expression language your version supports and the scope a variable is read from both matter. Confirm which expression language is enabled, whether conditions can call Java beans or services, and whether a variable set on a subprocess or call activity is visible at the gateway. Write one test per condition that uses a missing or null variable.
Database type and concurrent tokens
Parallel joins and jobs that release waiting tokens depend on transactional writes to the database. Flowable supports H2, MySQL, Oracle, PostgreSQL, DB2 and Microsoft SQL Server, and MySQL versions below 5.6.4 lack millisecond precision, which affects timer-based gateway paths. Confirm your database type and version, isolation settings and connection pool size, then test two tokens arriving at the same join at the same time.
REST API access to waiting executions
If operations staff or a support dashboard need to see which tokens are waiting at a join or event-based gateway, confirm that your deployment exposes the REST application or the Java runtime service to them. Decide who may query and move executions, and whether that access is restricted to a service account. Without this decision, a token stuck at an inclusive join can be found only by reading database tables.
Extensions beyond standard BPMN gateways
Decide whether your models need anything beyond standard BPMN gateway semantics, such as custom condition classes, listeners that set default flows, or Flowable-specific extension elements. Each extension couples the model to a Flowable version and to the code that ships with it. Ask whether the gateway logic can stay in the standard model, and if an extension is needed, record which engine version it was tested against.
Async executor and lock recovery
Gateways that wait on timers or asynchronous continuations rely on the async executor, which stores lock owners and lock expiration times in the database so that other nodes can pick up expired jobs. Confirm how many nodes will run the executor, the thread pool size on each, and the lock expiration setting. A node that stops without releasing its locks can delay a waiting path until the lock expires.
How DeployOpen scopes and delivers Flowable gateway work
DeployOpen plans, implements, tests and documents Flowable gateway logic inside your own Java stack and infrastructure, starting from the process models your teams already run. Engagements are fixed-scope, with deliverables and timelines agreed in writing before any build begins.
How we scope
- Review each BPMN model to map every exclusive, parallel and inclusive gateway to a documented business decision.
- Agree in writing which conditions, default flows and join rules the engagement covers for each gateway.
- Identify where a gateway depends on variables, decision tables or external services that need their own owner.
How we build
- Implement conditional and default flows with expressions that are tested against boundary and null variable values.
- Pair every parallel fork with a join, and verify token counts with engine-level tests before release.
- Build inclusive gateway conditions with explicit fallbacks so no branch is left without a path to completion.
How we hand over
- Deliver gateway test suites, model diagrams and a written register of conditions, defaults and join behavior.
- Document the runbook steps for finding instances whose tokens are waiting at a join or gateway.
- Walk your engineers through the model changes and the review checklist used when gateways are edited later.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.
- Retainers can cover gateway reviews when new process versions change branch conditions or join rules.
- Ask any supplier to confirm how Flowable release changes will be tested against your existing gateway models.

