flowable/flowable-engine · error · FlowableObjectNotFoundException
Execution ' ' does not have a variable ' ' in scope
Error message
Execution '${executionId}' does not have a variable '${variableName}' in scope ${scope} What it means
DELETE on a process-instance variable checks hasVariableOnScope for the execution and the requested scope (local by default, or 'global'/'shared'). If the execution does not have that variable in that scope, Flowable throws FlowableObjectNotFoundException naming the execution, variable, and scope.
Solutions
- Fetch GET /runtime/process-instances/{id}/variables first and confirm the variable name and its scope.
- Add the correct scope query parameter, e.g. ?scope=global, when the variable is on the process instance rather than local.
- Check for typos or case differences in variableName.
- Make deletion idempotent in client code: treat 404 as already-deleted and skip.
Example fix
// before curl -X DELETE .../process-instances/123/variables/orderId // variable is global // after curl -X DELETE .../process-instances/123/variables/orderId?scope=global
Defensive patterns
Strategy: try-catch
Validate before calling
const vars = await api.get(`/process-instances/${id}/variables`);
const v = vars.data.find(v => v.name === variableName);
if (!v) throw new Error(`Variable ${variableName} not present; nothing to delete`);
const scopeParam = v.scope === 'global' ? '?scope=global' : ''; Type guard
function hasVariable(vars, name) { return Array.isArray(vars) && vars.some(v => v.name === name); } Try / catch
try {
await api.delete(`/process-instances/${id}/variables/${name}${scopeParam}`);
} catch (e) {
if (e.response?.status === 404) { /* already gone; treat as idempotent success */ }
else throw e;
} Prevention
- List variables (with scope) before deleting.
- Remember the default scope is local; pass ?scope=global for process-instance-level variables.
- Make deletes idempotent by swallowing 404s.
When it happens
Trigger: DELETE /runtime/process-instances/{id}/variables/{variableName} (optionally ?scope=local|global) where the variable was never set, was already deleted, exists only in the other scope (e.g. variable is global but default scope=local is queried), or the execution id is wrong.
Common situations: Deleting a variable inherited from the process instance while omitting scope=global, double-delete retries, typos in variable names, variables set on a parent execution rather than the addressed one.
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
- Historic process instance '" + processInstanceId + "'…
- Batch part with id ' ' does not have a batch part document.
- Batch with id ' ' does not have a batch document.
- Cannot set global variables on execution
- Case instance ' ' doesn't have a variable with name: ' '.
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/6cb7b0ea52f21df4.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceVariableResource.java:194
@ApiOperation(value = "Delete a variable", tags = { "Process Instance Variables" }, nickname = "deleteProcessInstanceVariable", code = 204)
@ApiResponses(value = {
@ApiResponse(code = 204, message = "Indicates the variable was found and has been deleted. Response-body is intentionally empty."),
@ApiResponse(code = 404, message = "Indicates the requested variable was not found.")
})
@DeleteMapping(value = "/runtime/process-instances/{processInstanceId}/variables/{variableName}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteVariable(@ApiParam(name = "processInstanceId") @PathVariable("processInstanceId") String processInstanceId, @ApiParam(name = "variableName") @PathVariable("variableName") String variableName,
@RequestParam(value = "scope", required = false) String scope) {
Execution execution = getExecutionFromRequestWithoutAccessCheck(processInstanceId);
// Determine scope
RestVariableScope variableScope = RestVariableScope.LOCAL;
if (scope != null) {
variableScope = RestVariable.getScopeFromString(scope);
}
if (!hasVariableOnScope(execution, variableName, variableScope)) {
throw new FlowableObjectNotFoundException("Execution '" + execution.getId() + "' does not have a variable '" + variableName + "' in scope " + variableScope.name().toLowerCase(),
VariableInstanceEntity.class);
}
if (restApiInterceptor != null) {
restApiInterceptor.deleteExecutionVariables(execution, Collections.singleton(variableName), variableScope);
}
if (variableScope == RestVariableScope.LOCAL) {
runtimeService.removeVariableLocal(execution.getId(), variableName);
} else {
// Safe to use parentId, as the hasVariableOnScope would have
// stopped a global-var update on a root-execution
runtimeService.removeVariable(execution.getParentId(), variableName);
}
}
@Override
protected RestVariable constructRestVariable(String variableName, Object value, RestVariableScope variableScope, String executionId, boolean includeBinary) {View on GitHub (pinned to d6d39ce1c6)