{"record":{"id":"69b601666035dcb8","repo":"flowable/flowable-engine","slug":"attachment-with-id-attachmentid-does-not-have","errorCode":null,"errorMessage":"Attachment with id '{attachmentId}' does not have content associated with it.","messagePattern":"Attachment with id '(.+?)' does not have content associated with it\\.","errorType":"http","errorClass":"FlowableObjectNotFoundException","httpStatus":404,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskAttachmentContentResource.java","lineNumber":65,"sourceCode":"    @ApiOperation(value = \"Get the content for an attachment\", tags = { \"Task Attachments\" },\n            notes = \"The response body contains the binary content. By default, the content-type of the response is set to application/octet-stream unless the attachment type contains a valid Content-type.\")\n    @ApiResponses(value = {\n            @ApiResponse(code = 200, message = \"Indicates the task and attachment was found and the requested content is returned.\"),\n            @ApiResponse(code = 404, message = \"Indicates the requested task was not found or the task does not have an attachment with the given id or the attachment does not have a binary stream available. Status message provides additional information.\")\n    })\n    @GetMapping(value = \"/runtime/tasks/{taskId}/attachments/{attachmentId}/content\")\n    public ResponseEntity<byte[]> getAttachmentContent(@ApiParam(name = \"taskId\") @PathVariable(\"taskId\") String taskId, @ApiParam(name = \"attachmentId\") @PathVariable(\"attachmentId\") String attachmentId, HttpServletResponse response) {\n\n        HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);\n        Attachment attachment = taskService.getAttachment(attachmentId);\n\n        if (attachment == null || !task.getId().equals(attachment.getTaskId())) {\n            throw new FlowableObjectNotFoundException(\"Task '\" + task.getId() + \"' does not have an attachment with id '\" + attachmentId + \"'.\", Attachment.class);\n        }\n\n        InputStream attachmentStream = taskService.getAttachmentContent(attachmentId);\n        if (attachmentStream == null) {\n            throw new FlowableObjectNotFoundException(\"Attachment with id '\" + attachmentId + \"' does not have content associated with it.\", Attachment.class);\n        }\n\n        HttpHeaders responseHeaders = new HttpHeaders();\n        MediaType mediaType = null;\n        if (attachment.getType() != null) {\n            try {\n                mediaType = MediaType.valueOf(attachment.getType());\n                responseHeaders.set(\"Content-Type\", attachment.getType());\n            } catch (Exception e) {\n                // ignore if unknown media type\n            }\n        }\n\n        if (mediaType == null) {\n            responseHeaders.set(\"Content-Type\", \"application/octet-stream\");\n        }\n\n        try {","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskAttachmentContentResource.java#L47-L83","documentation":"FlowableObjectNotFoundException thrown when an attachment exists and belongs to the task, but taskService.getAttachmentContent returns null — i.e. the attachment metadata exists without associated binary content. Attachments can be created with metadata only (name/description/url), so content retrieval legitimately finds nothing.","triggerScenarios":"GET .../attachments/{attachmentId}/content on an attachment created without content (metadata/url-only attachment), or whose stored content was removed by cleanup/archival.","commonSituations":"Attachments created via the non-binary endpoint that only carry an external URL; content store purged or not migrated after a version upgrade; attachment persisted to a content store later cleared.","solutions":["Use the attachment's externalUrl (if present) instead of fetching content","Create the attachment via the binary endpoint so content is stored","Check the content store configuration and whether content was purged","After upgrades, verify content-store data was migrated"],"exampleFix":"// before\nPOST /runtime/tasks/{id}/attachments  {\"name\":\"x\",\"url\":\"http://...\"}  // metadata only\nGET  /runtime/tasks/{id}/attachments/{attId}/content  // 404 no content\n// after\nPOST /runtime/tasks/{id}/attachments  -F 'file=@doc.pdf'  // binary upload\nGET  /runtime/tasks/{id}/attachments/{attId}/content","handlingStrategy":"fallback","validationCode":"const att = await api.getTaskAttachment(taskId, attachmentId);\nif (att.url) { window.open(att.url); return; } // metadata-only attachment has externalUrl","typeGuard":"function hasExternalContent(att) { return typeof att.url === 'string' && att.url.length > 0; }","tryCatchPattern":"try { return await api.getAttachmentContent(taskId, attachmentId); } catch (e) { if (e.status === 404) { window.open(att.url || '/no-content'); return null; } throw e; }","preventionTips":["Prefer the binary upload endpoint when content must be retrievable","Check the attachment's externalUrl field before requesting content","Verify content-store retention policies don't purge needed content"],"tags":["rest","not-found","attachments","content-store"],"backgroundTag":"record-not-found","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}