flowable/flowable-engine · error · FlowableObjectNotFoundException
Task '${taskId}' does not have a variable '${variableName}'
Error message
Task '${taskId}' does not have a variable '${variableName}' in scope ${scope} What it means
Deleting a task variable (DELETE /runtime/tasks/{taskId}/variables/{variableName}) first checks that the variable exists in the requested scope (local by default, or global when ?scope=global). If the task does not have that variable in that scope, FlowableObjectNotFoundException is thrown (with VariableInstanceEntity as the missing type), mapping to HTTP 404. It prevents deleting non-existent variables and distinguishes local vs. process-level (global) scopes.
Source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskVariableResource.java:153
@ApiResponse(code = 204, message = "Indicates the task variable was found and has been deleted. Response-body is intentionally empty."),
@ApiResponse(code = 404, message = "Indicates the requested task was not found or the task does not have a variable with the given name. Status message contains additional information about the error.")
})
@DeleteMapping(value = "/runtime/tasks/{taskId}/variables/{variableName}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteVariable(@ApiParam(name = "taskId") @PathVariable("taskId") String taskId,
@ApiParam(name = "variableName") @PathVariable("variableName") String variableName,
@ApiParam(hidden = true) @RequestParam(value = "scope", required = false) String scopeString) {
Task task = getTaskFromRequestWithoutAccessCheck(taskId);
// Determine scope
RestVariableScope scope = RestVariableScope.LOCAL;
if (scopeString != null) {
scope = RestVariable.getScopeFromString(scopeString);
}
if (!hasVariableOnScope(task, variableName, scope)) {
throw new FlowableObjectNotFoundException("Task '" + task.getId() + "' does not have a variable '" + variableName + "' in scope " + scope.name().toLowerCase(), VariableInstanceEntity.class);
}
if (restApiInterceptor != null) {
restApiInterceptor.deleteTaskVariables(task, Collections.singleton(variableName), scope);
}
if (scope == RestVariableScope.LOCAL) {
taskService.removeVariableLocal(task.getId(), variableName);
} else {
// Safe to use executionId, as the hasVariableOnScope would have
// stopped a global-var update on standalone task
runtimeService.removeVariable(task.getExecutionId(), variableName);
}
}
}
View on GitHub (pinned to d6d39ce1c6)
Solutions
- Add ?scope=global when the variable was set at the process/execution level rather than on the task.
- GET the task's variables first to confirm the exact name and scope before deleting.
- Fix the task id / variable name (names are case-sensitive).
- Treat 404 as idempotent success if deletion may have already happened.
Example fix
// before: variable is global (set on process), default local scope fails curl -X DELETE .../runtime/tasks/123/variables/orderStatus // after curl -X DELETE .../runtime/tasks/123/variables/orderStatus?scope=global
Defensive patterns
Strategy: validation
Validate before calling
const vars = await fetch(`/runtime/tasks/${taskId}/variables?scope=${scope}`).then(r => r.json());
if (!vars.some(v => v.name === variableName)) {
return; // nothing to delete; skip the DELETE call
} Try / catch
try {
const resp = await fetch(url, { method: 'DELETE' });
if (resp.status === 404) return; // idempotent: already gone or wrong scope
if (!resp.ok) throw new Error(await resp.text());
} catch (e) {
if (!isNotFound(e)) throw e;
} Prevention
- List the task's variables (with scope) before deleting to confirm existence.
- Remember process-level variables need ?scope=global; the default scope is local.
- Treat 404 as success in cleanup/retry logic to stay idempotent.
- Variable names are case-sensitive — verify exact spelling.
When it happens
Trigger: DELETE /runtime/tasks/{taskId}/variables/{variableName}[?scope=local|global] where the variable is not present on the task in the given scope: variable was already deleted, variable exists only at process/execution (global) scope while scope=local is used, name typo, or variable belongs to a different task.
Common situations: Variables set at process start are global, but the default scope for this call is local, so deleting them from a task with default scope 404s; double-delete in retry logic; wrong task id; case-sensitive name mismatch.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- The variable does not have a binary data stream.
- The variable does not have a binary data stream.
- Could not find a resource with id '${resourceName}' in deplo
- Could not find the requested identity link.
- Variable name in the body should be equal to the name used i
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/30c82659374891f2.
Report an issue: GitHub.