flowable/flowable-engine · error · FlowableForbiddenException

Cannot delete a task that is part of a process instance.

Error message

Cannot delete a task that is part of a process instance.

What it means

The CMMN REST task endpoint also rejects deleting a task that is attached to a BPMN process instance (task has a non-null executionId). Even though this is the CMMN REST module, shared tasks with process scope are protected the same way: the engine owns their lifecycle. FlowableForbiddenException (HTTP 403) is thrown.

Source

Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/task/TaskResource.java:162

            @ApiImplicitParam(name = "cascadeHistory", dataType = "string", value = "Whether or not to delete the HistoricTask instance when deleting the task (if applicable). If not provided, this value defaults to false.", paramType = "query"),
            @ApiImplicitParam(name = "deleteReason", dataType = "string", value = "Reason why the task is deleted. This value is ignored when cascadeHistory is true.", paramType = "query")
    })
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "Indicates the task was found and has been deleted. Response-body is intentionally empty."),
            @ApiResponse(code = 403, message = "Indicates the requested task cannot be deleted because it’s part of a workflow."),
            @ApiResponse(code = 404, message = "Indicates the requested task was not found.")
    })
    @DeleteMapping(value = "/cmmn-runtime/tasks/{taskId}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteTask(@ApiParam(name = "taskId") @PathVariable String taskId, @ApiParam(hidden = true) @RequestParam(value = "cascadeHistory", required = false) Boolean cascadeHistory,
            @ApiParam(hidden = true) @RequestParam(value = "deleteReason", required = false) String deleteReason) {

        Task taskToDelete = getTaskFromRequestWithoutAccessCheck(taskId);
        if (taskToDelete.getScopeId() != null && ScopeTypes.CMMN.equals(taskToDelete.getScopeType())) {
            // Can't delete a task that is part of a case instance
            throw new FlowableForbiddenException("Cannot delete a task that is part of a case instance.");
        } else if (taskToDelete.getExecutionId() != null) {
            throw new FlowableForbiddenException("Cannot delete a task that is part of a process instance.");
        }
        
        if (restApiInterceptor != null) {
            restApiInterceptor.deleteTask(taskToDelete);
        }

        if (cascadeHistory != null) {
            // Ignore delete-reason since the task-history (where the reason is
            // recorded) will be deleted anyway
            taskService.deleteTask(taskToDelete.getId(), cascadeHistory);
        } else {
            // Delete with delete-reason
            taskService.deleteTask(taskToDelete.getId(), deleteReason);
        }
    }
    
    @ApiOperation(value = "Get a task form", tags = { "Tasks" })
    @ApiResponses(value = {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Complete the task (POST action 'complete') or end the process instance instead of deleting it.
  2. Delete the process instance via the process REST API (DELETE /runtime/process-instances/{id}) if the whole instance should go.
  3. Skip tasks with a non-null executionId in deletion scripts.
  4. Use the correct REST module (flowable-rest process endpoints) for process-scoped task operations.

Example fix

// before
curl -X DELETE /flowable-rest/cmmn-runtime/tasks/123
// throws: Cannot delete a task that is part of a process instance.

// after
curl -X POST /flowable-rest/cmmn-runtime/tasks/123 -H 'Content-Type: application/json' -d '{"action":"complete"}'
Defensive patterns

Strategy: type-guard

Validate before calling

const task = await api.get(`/cmmn-runtime/tasks/${id}`);
if (task.executionId) throw new Error('task belongs to a process instance; use process APIs');

Type guard

function isProcessTask(t) { return !!t.executionId; }

Try / catch

try { await api.delete(`/cmmn-runtime/tasks/${id}`); } catch (e) { if (e.response && e.response.status === 403) { /* route to process instance cleanup */ } else { throw e; } }

Prevention

When it happens

Trigger: Calling DELETE /cmmn-runtime/tasks/{taskId} where taskToDelete.getExecutionId() != null, i.e. the task was created by a running BPMN process but accessed through the CMMN REST API.

Common situations: Mixing the process and case REST APIs against a shared task; cleanup jobs iterating tasks without checking executionId; deleting a user task that belongs to an active process instance.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/c3d032a6776fcf28. Report an issue: GitHub.