flowable/flowable-engine · error · FlowableObjectNotFoundException

Could not find task instance with id:

Error message

Could not find task instance with id:

What it means

After fetching the tasks for bulkUpdateTasks, if the number of found tasks differs from the number of requested taskIds, at least one id was not found. The method removes the found ids from the request list, leaving only missing ids, and throws FlowableObjectNotFoundException listing them comma-separated.

Solutions

  1. Validate all taskIds exist via the task query API before the bulk update
  2. Remove or correct the missing ids listed in the exception message (the leftover ids after found ones are removed)
  3. Avoid duplicate ids in taskIds
  4. Catch FlowableObjectNotFoundException, parse the listed ids, and retry with only the existing tasks

Example fix

// before
restClient.bulkUpdate(taskIds) // taskIds contains completed task t9
// after
List<String> valid = taskIds.stream().filter(id -> taskExists(id)).collect(toList());
restClient.bulkUpdate(valid);
Defensive patterns

Strategy: validation

Validate before calling

const existing = await queryTasks({ taskIds }); if (existing.length !== new Set(taskIds).size) throw new NotFound('some taskIds missing');

Try / catch

try { await bulkUpdate(ids); } catch (e) { if (e.message.startsWith('Could not find task instance with id')) { /* parse missing ids, filter them out, retry */ } throw e; }

Prevention

When it happens

Trigger: PUT /cmmn-runtime/tasks where any id in taskIds refers to a nonexistent, completed/deleted, or foreign-tenant task; duplicate ids can also contribute to count mismatch.

Common situations: Bulk update over ids collected earlier, some since completed; mixed environments (test vs prod ids); duplicate entries in taskIds.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

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

    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "Indicates request was successful and the tasks are returned"),
            @ApiResponse(code = 400, message = "Indicates a parameter was passed in the wrong format or that delegationState has an invalid value (other than pending and resolved). The status-message contains additional information.")
    })
    @PutMapping(value = "/cmmn-runtime/tasks", produces = "application/json")
    public DataResponse<TaskResponse> bulkUpdateTasks(@RequestBody BulkTasksRequest bulkTasksRequest) {

        if (bulkTasksRequest == null) {
            throw new FlowableException("A request body was expected when bulk updating tasks.");
        }
        if (bulkTasksRequest.getTaskIds() == null) {
            throw new FlowableIllegalArgumentException("taskIds can not be null for bulk update tasks requests");
        }

        Collection<Task> taskList = getTasksFromRequest(bulkTasksRequest.getTaskIds());

        if (taskList.size() != bulkTasksRequest.getTaskIds().size()) {
            taskList.stream().forEach(task -> bulkTasksRequest.getTaskIds().remove(task.getId()));
            throw new FlowableObjectNotFoundException(
                    "Could not find task instance with id:" + bulkTasksRequest.getTaskIds().stream().collect(Collectors.joining(",")));
        }

        // Populate the task properties based on the request
        populateTasksFromRequest(taskList, bulkTasksRequest);

        if (restApiInterceptor != null) {
            restApiInterceptor.bulkUpdateTasks(taskList, bulkTasksRequest);
        }

        // Save the task and fetch again, it's possible that an
        // assignment-listener has updated
        // fields after it was saved so we can not use the in-memory task
        taskService.bulkSaveTasks(taskList);

        List<Task> taskResultList = getTasksFromRequest(bulkTasksRequest.getTaskIds());

        DataResponse<TaskResponse> dataResponse = new DataResponse<>();

View on GitHub (pinned to d6d39ce1c6)