{"record":{"id":"be20c1007aab7b84","repo":"flowable/flowable-engine","slug":"unexpected-exception-getting-variable-data-be20c1","errorCode":null,"errorMessage":"Unexpected exception getting variable data","messagePattern":"Unexpected exception getting variable data","errorType":"http","errorClass":"FlowableException","httpStatus":500,"severity":"error","filePath":"modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/history/task/HistoricTaskInstanceVariableDataResource.java","lineNumber":91,"sourceCode":"                result = (byte[]) variable.getValue();\n                response.setContentType(\"application/octet-stream\");\n\n            } else if (CmmnRestResponseFactory.SERIALIZABLE_VARIABLE_TYPE.equals(variable.getType())) {\n                ByteArrayOutputStream buffer = new ByteArrayOutputStream();\n                ObjectOutputStream outputStream = new ObjectOutputStream(buffer);\n                outputStream.writeObject(variable.getValue());\n                outputStream.close();\n                result = buffer.toByteArray();\n                response.setContentType(\"application/x-java-serialized-object\");\n\n            } else {\n                throw new FlowableObjectNotFoundException(\"The variable does not have a binary data stream.\", null);\n            }\n            return result;\n\n        } catch (IOException ioe) {\n            // Re-throw IOException\n            throw new FlowableException(\"Unexpected exception getting variable data\", ioe);\n        }\n    }\n\n    public RestVariable getVariableFromRequest(boolean includeBinary, String taskId, String variableName, String scope) {\n        RestVariableScope variableScope = RestVariable.getScopeFromString(scope);\n        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().taskId(taskId);\n\n        if (variableScope != null) {\n            if (variableScope == RestVariableScope.GLOBAL) {\n                taskQuery.includeProcessVariables();\n            } else {\n                taskQuery.includeTaskLocalVariables();\n            }\n        } else {\n            taskQuery.includeTaskLocalVariables().includeProcessVariables();\n        }\n\n        HistoricTaskInstance taskObject = taskQuery.singleResult();","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/history/task/HistoricTaskInstanceVariableDataResource.java#L73-L109","documentation":"FlowableException (generic runtime) wrapping an IOException that occurred while serializing a variable's value to the response byte stream. When a historic variable's value is Serializable, the resource writes it with an ObjectOutputStream into a byte buffer; if serialization fails (IO error), this wrapper is thrown. It indicates a server-side serialization failure, not a client mistake.","triggerScenarios":"GET .../variables/{variableName}/data on a Serializable variable whose class (or a nested field's class) fails to serialize — typically because the variable's class is not on the server classpath or has an incompatible serialVersionUID at read time.","commonSituations":"Serializable custom classes stored in variables where the class was later refactored (serialVersionUID changed) and the old engine data can't be deserialized; missing application class on the server for a legacy variable; JDK serialization incompatibility after Java upgrade.","solutions":["Check the cause chain (FlowableException wraps the IOException) for NotSerializableException/InvalidClassException to identify the offending class","Ensure the custom Serializable class is on the server classpath with a matching serialVersionUID","Store variables as byte[] or JSON strings instead of raw Serializable custom objects to avoid JDK serialization coupling","Handle this error client-side as a 500 and fall back to the plain variable endpoint"],"exampleFix":"// before\nprocessVariables.put(\"payload\", new LegacyPojo(field)); // serialVersionUID mismatch later\n// after\nprocessVariables.put(\"payload\", jsonBytes); // store byte[]/String to avoid JDK serialization","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await fetchVariableData(taskId, name);\n} catch (e) {\n  if (String(e.message).includes('Unexpected exception getting variable data')) {\n    log.error('Serialization failure; cause=' + e.cause); // inspect NotSerializableException / InvalidClassException\n    return fetchPlainVariable(taskId, name);\n  }\n  throw e;\n}","preventionTips":["Avoid storing raw Serializable custom objects in variables; prefer byte[] or JSON strings","Keep serialVersionUID stable in classes stored as variables and keep them on the server classpath","Read the wrapped IOException cause to identify the failing class","Test variable download endpoints after class refactorings and JDK upgrades"],"tags":["serialization","rest","variables","historical-data"],"backgroundTag":"json-marshal-failed","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}