{"record":{"id":"823f24736012f9c4","repo":"flowable/flowable-engine","slug":"cannot-delete-a-task-that-is-part-of-a-process-ins-823f24","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-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskResource.java","lineNumber":159,"sourceCode":"    @ApiOperation(value = \"Delete a task\", tags = {\"Tasks\"}, code = 204)\n    @ApiImplicitParams({\n            @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 = \"/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.getExecutionId() != null) {\n            // Can not delete a task that is part of a process instance\n            throw new FlowableForbiddenException(\"Cannot delete a task that is part of a process instance.\");\n        } else if (taskToDelete.getScopeId() != null && ScopeTypes.CMMN.equals(taskToDelete.getScopeType())) {\n            throw new FlowableForbiddenException(\"Cannot delete a task that is part of a case 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","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskResource.java#L141-L177","documentation":"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.","triggerScenarios":"DELETE /runtime/tasks/{taskId} for a task that appears in a process instance; the URL query params cascadeHistory/deleteReason are irrelevant here.","commonSituations":"Automated cleanup scripts iterating all runtime tasks and deleting them, hitting process-created tasks; confusion between standalone and process-scoped task lifecycle.","solutions":["Complete the task instead (POST /runtime/tasks/{taskId} with {\"action\":\"complete\"})","If the whole flow should go away, delete the process instance: DELETE /runtime/process-instances/{processInstanceId}","Filter task queries to standalone tasks (executionId null) before deleting"],"exampleFix":"// before\nif (task.getExecutionId() == null) restTemplate.delete(TASK_URL + \"/\" + task.getId());\nrestTemplate.delete(TASK_URL + \"/\" + task.getId()); // throws for process tasks\n// after\nif (task.getExecutionId() == null) restTemplate.delete(TASK_URL + \"/\" + task.getId());\nelse completeTask(task.getId());","handlingStrategy":"validation","validationCode":"const task = await get('/runtime/tasks/'+taskId); if (task.executionId != null) { /* complete instead of delete */ }","typeGuard":"function isStandaloneTask(t) { return t.executionId == null; }","tryCatchPattern":"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; }","preventionTips":["Check executionId before any runtime task delete","Prefer completing process tasks over deleting them","Restrict bulk cleanup jobs to standalone tasks"],"tags":["rest","forbidden","task-lifecycle","process-instance"],"backgroundTag":"unsupported-operation","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}