flowable/flowable-engine · error · FlowableObjectNotFoundException

The variable does not have a binary data stream.

Error message

The variable does not have a binary data stream.

What it means

Thrown by the Flowable REST history API when a request asks for the binary data of a historic task variable, but the variable's value is not a byte array (or otherwise cannot be streamed as binary content). Only byte[]-valued variables can be returned as a binary data stream; serializable values are Java-serialized and non-binary values have nothing to stream. The error is raised as FlowableObjectNotFoundException because the requested binary content effectively does not exist for this variable.

Source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/history/HistoricTaskInstanceVariableDataResource.java:89

            HttpServletResponse response) {

        try {
            byte[] result = null;
            RestVariable variable = getVariableFromRequest(true, taskId, variableName, scope);
            if (RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE.equals(variable.getType())) {
                result = (byte[]) variable.getValue();
                response.setContentType("application/octet-stream");

            } else if (RestResponseFactory.SERIALIZABLE_VARIABLE_TYPE.equals(variable.getType())) {
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                ObjectOutputStream outputStream = new ObjectOutputStream(buffer);
                outputStream.writeObject(variable.getValue());
                outputStream.close();
                result = buffer.toByteArray();
                response.setContentType("application/x-java-serialized-object");

            } else {
                throw new FlowableObjectNotFoundException("The variable does not have a binary data stream.", null);
            }
            return result;

        } catch (IOException ioe) {
            // Re-throw IOException
            throw new FlowableException("Unexpected exception getting variable data", ioe);
        }
    }

    public RestVariable getVariableFromRequest(boolean includeBinary, String taskId, String variableName, String scope) {
        RestVariableScope variableScope = RestVariable.getScopeFromString(scope);
        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().taskId(taskId);

        if (variableScope != null) {
            if (variableScope == RestVariableScope.GLOBAL) {
                taskQuery.includeProcessVariables();
            } else {
                taskQuery.includeTaskLocalVariables();

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Verify the variable is actually a byte[] variable by fetching its metadata via GET /history/historic-task-instances/{taskId}/variables/{variableName} and checking the 'type'/'valueUrl' fields
  2. Only call the /data sub-resource for binary variables; for simple values read the value directly from the variable JSON response
  3. If the value should be binary, fix the process that created the variable to store a byte[] (or Serializable) instead of a String
  4. Catch FlowableObjectNotFoundException on the client and fall back to reading the plain variable value

Example fix

// before
defaultHttpClient.get('/flowable-rest/history/historic-task-instances/' + taskId + '/variables/' + name + '/data')
// after
const meta = await defaultHttpClient.get('/flowable-rest/history/historic-task-instances/' + taskId + '/variables/' + name)
if (meta.data.type === 'binary' || meta.data.valueUrl) {
  return defaultHttpClient.get(meta.data.valueUrl, { responseType: 'arraybuffer' })
}
return meta.data.value
Defensive patterns

Strategy: validation

Validate before calling

const meta = await api.get(`/history/historic-task-instances/${taskId}/variables/${name}`);
if (!meta.data.valueUrl || meta.data.type !== 'binary') throw new SkipBinaryDownloadError(meta.data.type);

Type guard

function isBinaryVariable(meta) { return meta && (meta.type === 'binary' || typeof meta.valueUrl === 'string'); }

Try / catch

try { return await api.get(url, {responseType:'arraybuffer'}); } catch (e) { if (e.response && e.response.status === 404) return getPlainVariableValue(taskId, name); throw e; }

Prevention

When it happens

Trigger: Calling GET /history/historic-task-instances/{taskId}/variables/{variableName}/data when the historic variable's value is not a byte[] (e.g. a plain String, Long, or Date), so the resource's getVariableData falls into the else branch and throws instead of streaming bytes.

Common situations: Front-end code assumes every /data endpoint returns raw bytes and points it at ordinary primitive variables; scripts exporting task attachments call /data on variables that were stored as JSON strings rather than byte arrays; API clients reuse a download URL template for all variable types.

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/24556494cc1baa47. Report an issue: GitHub.