flowable/flowable-engine · error · FlowableObjectNotFoundException

Task ' ' doesn't have a variable ' ' in scope

Error message

Task '${taskId}' doesn't have a variable '${variableName}' in scope ${scope}

What it means

This FlowableObjectNotFoundException is thrown by the CMMN REST API when a DELETE request for a task variable cannot find the named variable on the task in the requested scope (LOCAL by default, or GLOBAL/ANY). The API validates that the variable exists before deleting it, so deleting a non-existent variable fails rather than silently succeeding.

Solutions

  1. Set the correct scope query parameter (e.g. ?scope=global) if the variable lives in the global/case scope
  2. Verify the variable name via GET /cmmn-runtime/tasks/{taskId}/variables before deleting
  3. Check the task is still active; completed/terminated tasks may have purged variables
  4. Handle 404 in the client as an idempotent success if the goal is just to remove the variable

Example fix

// before
delete /cmmn-runtime/tasks/{taskId}/variables/orderId
// after
delete /cmmn-runtime/tasks/{taskId}/variables/orderId?scope=global
Defensive patterns

Strategy: try-catch

Validate before calling

const vars = await fetch(`/cmmn-runtime/tasks/${taskId}/variables`); const exists = (await vars.json()).some(v => v.name === variableName);

Type guard

function hasVariable(vars, name, scope) { return Array.isArray(vars) && vars.some(v => v.name === name && (scope === undefined || v.scope === undefined || v.scope === scope)); }

Try / catch

try { await deleteVariable(taskId, variableName, scope); } catch (e) { if (e.status === 404 && /doesn't have a variable/.test(e.message)) { /* treat as already deleted */ } else { throw e; } }

Prevention

When it happens

Trigger: DELETE /cmmn-runtime/tasks/{taskId}/variables/{variableName} (optionally with ?scope=global|local) where the task has no variable with that name in the requested scope. Note default scope is LOCAL, so a global-only variable will also trigger this if scope is not specified.

Common situations: Client caches stale variable lists after the variable was already deleted; variable exists only in GLOBAL scope but request omits scope=global; typo in variable name; task already completed and its local variables were removed.

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


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

Appendix: source

Thrown at modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/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 = "/cmmn-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() + "' doesn't 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 scope id, as the hasVariableOnScope would have
            // stopped a global-var update on standalone task
            runtimeService.removeVariable(task.getScopeId(), variableName);
        }
    }
}

View on GitHub (pinned to d6d39ce1c6)