{"record":{"id":"c3d032a6776fcf28","repo":"flowable/flowable-engine","slug":"cannot-delete-a-task-that-is-part-of-a-process-ins","errorCode":null,"errorMessage":"Cannot delete a task that is part of a process instance.","messagePattern":"Cannot delete a task that is part of a process instance\\.","errorType":"http","errorClass":"FlowableForbiddenException","httpStatus":403,"severity":"error","filePath":"modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/task/TaskResource.java","lineNumber":162,"sourceCode":"            @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\"),\n            @ApiImplicitParam(name = \"deleteReason\", dataType = \"string\", value = \"Reason why the task is deleted. This value is ignored when cascadeHistory is true.\", paramType = \"query\")\n    })\n    @ApiResponses(value = {\n            @ApiResponse(code = 204, message = \"Indicates the task was found and has been deleted. Response-body is intentionally empty.\"),\n            @ApiResponse(code = 403, message = \"Indicates the requested task cannot be deleted because it’s part of a workflow.\"),\n            @ApiResponse(code = 404, message = \"Indicates the requested task was not found.\")\n    })\n    @DeleteMapping(value = \"/cmmn-runtime/tasks/{taskId}\")\n    @ResponseStatus(HttpStatus.NO_CONTENT)\n    public void deleteTask(@ApiParam(name = \"taskId\") @PathVariable String taskId, @ApiParam(hidden = true) @RequestParam(value = \"cascadeHistory\", required = false) Boolean cascadeHistory,\n            @ApiParam(hidden = true) @RequestParam(value = \"deleteReason\", required = false) String deleteReason) {\n\n        Task taskToDelete = getTaskFromRequestWithoutAccessCheck(taskId);\n        if (taskToDelete.getScopeId() != null && ScopeTypes.CMMN.equals(taskToDelete.getScopeType())) {\n            // Can't delete a task that is part of a case instance\n            throw new FlowableForbiddenException(\"Cannot delete a task that is part of a case instance.\");\n        } else if (taskToDelete.getExecutionId() != null) {\n            throw new FlowableForbiddenException(\"Cannot delete a task that is part of a process instance.\");\n        }\n        \n        if (restApiInterceptor != null) {\n            restApiInterceptor.deleteTask(taskToDelete);\n        }\n\n        if (cascadeHistory != null) {\n            // Ignore delete-reason since the task-history (where the reason is\n            // recorded) will be deleted anyway\n            taskService.deleteTask(taskToDelete.getId(), cascadeHistory);\n        } else {\n            // Delete with delete-reason\n            taskService.deleteTask(taskToDelete.getId(), deleteReason);\n        }\n    }\n    \n    @ApiOperation(value = \"Get a task form\", tags = { \"Tasks\" })\n    @ApiResponses(value = {","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/task/TaskResource.java#L144-L180","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Complete the task (POST action 'complete') or end the process instance instead of deleting it.","Delete the process instance via the process REST API (DELETE /runtime/process-instances/{id}) if the whole instance should go.","Skip tasks with a non-null executionId in deletion scripts.","Use the correct REST module (flowable-rest process endpoints) for process-scoped task operations."],"exampleFix":"// before\ncurl -X DELETE /flowable-rest/cmmn-runtime/tasks/123\n// throws: Cannot delete a task that is part of a process instance.\n\n// after\ncurl -X POST /flowable-rest/cmmn-runtime/tasks/123 -H 'Content-Type: application/json' -d '{\"action\":\"complete\"}'","handlingStrategy":"type-guard","validationCode":"const task = await api.get(`/cmmn-runtime/tasks/${id}`);\nif (task.executionId) throw new Error('task belongs to a process instance; use process APIs');","typeGuard":"function isProcessTask(t) { return !!t.executionId; }","tryCatchPattern":"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; } }","preventionTips":["Skip tasks with non-null executionId in cleanup jobs","Use the process REST module for process-scoped tasks","Complete tasks rather than deleting engine-managed tasks"],"tags":["rest-api","cmmn","bpmn","forbidden","task-lifecycle"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}