flowable/flowable-engine · error · FlowableObjectNotFoundException

Task '

Error message

Task '

What it means

When reading a task variable via the CMMN REST API, if the variable is not found in the requested scope (global or local) the API throws FlowableObjectNotFoundException (HTTP 404) naming the task id and variable name. This distinguishes 'task exists but variable absent' from other errors.

Source

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

                    variableFound = true;
                }
            }

        } else if (variableScope == RestVariableScope.GLOBAL) {
            if (ScopeTypes.CMMN.equals(task.getScopeType()) && task.getScopeId() != null && runtimeService.hasVariable(task.getScopeId(), variableName)) {
                value = runtimeService.getVariable(task.getScopeId(), variableName);
                variableFound = true;
            }

        } else if (variableScope == RestVariableScope.LOCAL) {
            if (taskService.hasVariableLocal(taskId, variableName)) {
                value = taskService.getVariableLocal(taskId, variableName);
                variableFound = true;
            }
        }

        if (!variableFound) {
            throw new FlowableObjectNotFoundException("Task '" + taskId + "' doesn't have a variable with name: '" + variableName + "'.", VariableInstanceEntity.class);
        } else {
            return restResponseFactory.createRestVariable(variableName, value, variableScope, taskId, CmmnRestResponseFactory.VARIABLE_TASK, includeBinary);
        }
    }

    protected boolean hasVariableOnScope(Task task, String variableName, RestVariableScope scope) {
        boolean variableFound = false;

        if (scope == RestVariableScope.GLOBAL) {
            if (ScopeTypes.CMMN.equals(task.getScopeType()) && task.getScopeId() != null && runtimeService.hasVariable(task.getScopeId(), variableName)) {
                variableFound = true;
            }

        } else if (scope == RestVariableScope.LOCAL) {
            if (taskService.hasVariableLocal(task.getId(), variableName)) {
                variableFound = true;
            }
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Check available names first via GET /cmmn-runtime/tasks/{taskId}/variables.
  2. Add scope qualifier: use /variables/{name}/local for local variables.
  3. Handle 404 gracefully with a default value in the client.
  4. Fix the variable name spelling or set the variable before reading it.

Example fix

// before
GET /cmmn-runtime/tasks/123/variables/approvl  // 404

// after
GET /cmmn-runtime/tasks/123/variables/approval  // correct name
GET /cmmn-runtime/tasks/123/variables/approval/local  // if local-scoped
Defensive patterns

Strategy: try-catch

Validate before calling

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

Try / catch

try { return await api.get(`/cmmn-runtime/tasks/${taskId}/variables/${name}`); } catch (e) { if (e.response && e.response.status === 404) return defaultValue; throw e; }

Prevention

When it happens

Trigger: GET /cmmn-runtime/tasks/{taskId}/variables/{variableName} (or scoped variants) where the variable name never existed on the task, was set only in the other scope, or was deleted.

Common situations: Typos in variable names; querying a local-only variable without /local and vice versa; reading after task completion removed runtime variables; case/prefix mismatch between services.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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