Problem/Motivation
A webform that starts a workflow does it in the Orchestra interaction handler's postSave(), which runs after the submission is durably saved. That start is unguarded, and the engine throws when the named workflow cannot run: the submitter's answers are stored, no run begins, and they are shown an error page instead of a confirmation, with nothing in the log naming the form or the workflow.
The engine refuses a start for four reasons, every one of them before it commits anything: the workflow was deleted since the handler was pointed at it (InvalidArgumentException from DefinitionResolver::loadDefinition()), it is disabled, it has no valid start node, or it is not available in the tenant the submission acts in (each a LogicException from WorkflowExecutor::start()). The disabled case arrived with #3621295, which also stopped the handler's select offering a disabled workflow, so a form reaches that one by having been pointed at it before it was retired, or by the workflow being retired afterwards.
Nothing is corrupted. The start throws before bindSourceEntity() runs, so the submission is left unbound, which is the state postSave() reads as "started nothing": saving the submission again once the workflow can run does start the run, and does not create a second submission record. What is missing is telling anybody.
Steps to reproduce
- Point a webform's Orchestra interaction handler at a workflow.
- Disable that workflow, or delete it.
- Complete the form as a fresh submission.
The submission is saved, the submitter gets an error page, and no process instance exists.
Proposed resolution
Guard the start in postSave(): catch the refusal, tell the submitter their answers were saved but the request could not be started, and log it with the engine's own reason beside the webform, the workflow and the submission, for whoever has to fix the configuration. Leave the submission saved and unbound, so a later save picks it up once the workflow is back.
Cover it with tests that fail against the unguarded handler, including a cause the doorway does not name, so the guard is shown to catch the family the engine throws rather than an enumerated pair.
User interface changes
A submitter whose request cannot be started is told so, instead of meeting an error page.
API changes
None.
AI-Generated: Yes (Claude Code found this while auditing #3621295 and drafted this summary. The behaviour was read off the handler's postSave() and checked against WorkflowExecutor::start() and DefinitionResolver::loadDefinition(); the fix and its tests are on the merge request, and each test was confirmed to fail against the unguarded handler.)
Issue fork orchestra-3621650
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
mably commentedComment #4
mably commentedComment #6
mably commented