flowable/flowable-engine · error · FlowableException

A request body was expected when executing a task action.

Error message

A request body was expected when executing a task action.

What it means

POST /cmmn-runtime/tasks/{taskId} executes a task action (claim, complete, delegate, resolve) driven by a TaskActionRequest body. If the body is missing/null, executeTaskAction throws FlowableException because no action can be determined.

Solutions

  1. Send a body with an action field: {"action":"complete"} (also claim, delegate, resolve)
  2. Set Content-Type: application/json
  3. Check that the client library/model actually serializes the action request object

Example fix

// before
POST /cmmn-runtime/tasks/123        (empty body)
// after
POST /cmmn-runtime/tasks/123
{"action":"claim","assignee":"john"}
Defensive patterns

Strategy: validation

Validate before calling

const ACTIONS = ['claim','complete','delegate','resolve'];
function canExecuteAction(actionRequest) {
  return actionRequest != null && ACTIONS.includes(actionRequest.action);
}

Type guard

function hasAction(a) {
  return a != null && typeof a.action === 'string' && a.action.length > 0;
}

Try / catch

try {
  await post(`/cmmn-runtime/tasks/${taskId}`, actionRequest);
} catch (e) {
  if (/request body was expected/.test(e.body?.message ?? '')) {
    throw new Error('task action POST requires a JSON body with an action field');
  }
  throw e;
}

Prevention

When it happens

Trigger: POST to /cmmn-runtime/tasks/{taskId} with an empty body or without a JSON body containing the 'action' field.

Common situations: Empty POST issued to 'trigger' the task as if it were a signal endpoint; Content-Type not application/json so the body is dropped; copy-paste from endpoints that take no body.

Related errors


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

Appendix: source

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

        taskService.saveTask(task);
        task = taskService.createTaskQuery().taskId(task.getId()).singleResult();

        return restResponseFactory.createTaskResponse(task);
    }

    @ApiOperation(value = "Tasks actions", tags = { "Tasks" },
            notes = "")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Indicates the action was executed."),
            @ApiResponse(code = 400, message = "When the body contains an invalid value or when the assignee is missing when the action requires it."),
            @ApiResponse(code = 404, message = "Indicates the requested task was not found."),
            @ApiResponse(code = 409, message = "Indicates the action cannot be performed due to a conflict. Either the task was updates simultaneously or the task was claimed by another user, in case of the claim action.")
    })
    @PostMapping(value = "/cmmn-runtime/tasks/{taskId}")
    @ResponseStatus(value = HttpStatus.OK)
    public void executeTaskAction(@ApiParam(name = "taskId") @PathVariable String taskId, @RequestBody TaskActionRequest actionRequest) {
        if (actionRequest == null) {
            throw new FlowableException("A request body was expected when executing a task action.");
        }

        Task task = getTaskFromRequestWithoutAccessCheck(taskId);
        
        if (restApiInterceptor != null) {
            restApiInterceptor.executeTaskAction(task, actionRequest);
        }

        if (TaskActionRequest.ACTION_COMPLETE.equals(actionRequest.getAction())) {
            completeTask(task, actionRequest);

        } else if (TaskActionRequest.ACTION_CLAIM.equals(actionRequest.getAction())) {
            claimTask(task, actionRequest);

        } else if (TaskActionRequest.ACTION_UNCLAIM.equals(actionRequest.getAction())) {
            unclaimTask(task);

        } else if (TaskActionRequest.ACTION_DELEGATE.equals(actionRequest.getAction())) {

View on GitHub (pinned to d6d39ce1c6)