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

A FlowableForbiddenException thrown by deleteTask when the target runtime task has an executionId, meaning it was created by a running BPMN process instance. Standalone tasks can be deleted via the REST API, but process-linked tasks must be ended through the engine (e.g. complete) or by deleting the process instance.

Source

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

    @ApiOperation(value = "Delete a task", tags = {"Tasks"}, code = 204)
    @ApiImplicitParams({
            @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 = "/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.getExecutionId() != null) {
            // Can not delete a task that is part of a process instance
            throw new FlowableForbiddenException("Cannot delete a task that is part of a process instance.");
        } else if (taskToDelete.getScopeId() != null && ScopeTypes.CMMN.equals(taskToDelete.getScopeType())) {
            throw new FlowableForbiddenException("Cannot delete a task that is part of a case 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);
        }
    }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Complete the task instead (POST /runtime/tasks/{taskId} with {"action":"complete"})
  2. If the whole flow should go away, delete the process instance: DELETE /runtime/process-instances/{processInstanceId}
  3. Filter task queries to standalone tasks (executionId null) before deleting

Example fix

// before
if (task.getExecutionId() == null) restTemplate.delete(TASK_URL + "/" + task.getId());
restTemplate.delete(TASK_URL + "/" + task.getId()); // throws for process tasks
// after
if (task.getExecutionId() == null) restTemplate.delete(TASK_URL + "/" + task.getId());
else completeTask(task.getId());
Defensive patterns

Strategy: validation

Validate before calling

const task = await get('/runtime/tasks/'+taskId); if (task.executionId != null) { /* complete instead of delete */ }

Type guard

function isStandaloneTask(t) { return t.executionId == null; }

Try / catch

catch (e) { if (e.status === 403 && /part of a process instance/.test(e.body && e.body.message)) { /* complete the task or delete the process instance */ } else throw e; }

Prevention

When it happens

Trigger: DELETE /runtime/tasks/{taskId} for a task that appears in a process instance; the URL query params cascadeHistory/deleteReason are irrelevant here.

Common situations: Automated cleanup scripts iterating all runtime tasks and deleting them, hitting process-created tasks; confusion between standalone and process-scoped task lifecycle.

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/823f24736012f9c4. Report an issue: GitHub.