flowable/flowable-engine · error · FlowableIllegalArgumentException

Error converting request body to RestVariable instance

Error message

Error converting request body to RestVariable instance

What it means

For non-multipart PUT requests to update a task variable, Flowable deserializes the request body JSON into a RestVariable object with Jackson's ObjectMapper. FlowableIllegalArgumentException is thrown when the body cannot be parsed/converted (malformed JSON, wrong shape, encoding problems). The original exception is preserved as the cause.

Solutions

  1. Send a valid JSON body shaped like RestVariable: {"name":"var","type":"string","value":"x"}.
  2. Set header Content-Type: application/json on the request.
  3. Validate the JSON with a linter or by deserializing client-side before sending.
  4. Check the exception cause in server logs for the exact Jackson parse error.

Example fix

// before (form-encoded, not JSON)
curl -X PUT .../tasks/123/variables/name -d 'value=John'

// after
curl -X PUT -H 'Content-Type: application/json' .../tasks/123/variables/name -d '{"name":"name","type":"string","value":"John"}'
Defensive patterns

Strategy: validation

Validate before calling

function buildVariablePayload(name, type, value) {
  const payload = JSON.stringify({ name, type, value });
  JSON.parse(payload); // throws early if serialization produced invalid JSON
  return payload;
}

Type guard

function isRestVariable(obj) {
  return typeof obj === 'object' && obj !== null &&
    typeof obj.name === 'string' &&
    ('value' in obj) && typeof obj.type === 'string';
}

Try / catch

try {
  const resp = await fetch(url, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body });
  if (!resp.ok) throw new Error(await resp.text());
} catch (e) {
  if (String(e.message).includes('Error converting request body')) console.error('body was not valid RestVariable JSON:', body);
  throw e;
}

Prevention

When it happens

Trigger: PUT /runtime/tasks/{taskId}/variables/{variableName} with a Content-Type other than multipart whose input stream is not valid JSON for RestVariable: malformed JSON, missing quotes, wrong content type (e.g. form-encoded), empty body, or JSON with incompatible fields.

Common situations: Sending form-urlencoded data instead of JSON; forgetting Content-Type: application/json; trailing commas or syntax errors; sending XML; empty PUT body.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

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

        Task task = getTaskFromRequestWithoutAccessCheck(taskId);

        RestVariable result = null;
        if (request instanceof MultipartHttpServletRequest) {
            result = setBinaryVariable((MultipartHttpServletRequest) request, task, false);

            if (!result.getName().equals(variableName)) {
                throw new FlowableIllegalArgumentException("Variable name in the body should be equal to the name used in the requested URL.");
            }

        } else {

            RestVariable restVariable = null;

            try {
                restVariable = objectMapper.readValue(request.getInputStream(), RestVariable.class);
            } catch (Exception e) {
                throw new FlowableIllegalArgumentException("Error converting request body to RestVariable instance", e);
            }

            if (restVariable == null) {
                throw new FlowableException("Invalid body was supplied");
            }
            if (!restVariable.getName().equals(variableName)) {
                throw new FlowableIllegalArgumentException("Variable name in the body should be equal to the name used in the requested URL.");
            }

            result = setSimpleVariable(restVariable, task, false);
        }
        return result;
    }

    @ApiOperation(value = "Delete a variable on a task", tags = { "Task Variables" }, nickname = "deleteTaskInstanceVariable", code = 204)
    @ApiImplicitParams(@ApiImplicitParam(name = "scope", dataType = "string", value = "Scope of variable to be returned. When local, only task-local variable value is returned. When global, only variable value from the task’s parent execution-hierarchy are returned. When the parameter is omitted, a local variable will be returned if it exists, otherwise a global variable.", paramType = "query"))
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "Indicates the task variable was found and has been deleted. Response-body is intentionally empty."),

View on GitHub (pinned to d6d39ce1c6)