{"record":{"id":"e4213b5eb4d7017f","repo":"flowable/flowable-engine","slug":"task-taskid-does-not-have-an-attachment-with-i","errorCode":null,"errorMessage":"Task '{taskId}' does not have an attachment with id '{attachmentId}'.","messagePattern":"Task '(.+?)' does not have an attachment with id '(.+?)'\\.","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":60,"sourceCode":" */\n@RestController\n@Api(tags = { \"Task Attachments\" }, authorizations = { @Authorization(value = \"basicAuth\") })\npublic class TaskAttachmentContentResource extends TaskBaseResource {\n\n    @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","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskAttachmentContentResource.java#L42-L78","documentation":"FlowableObjectNotFoundException thrown when fetching the content of a task attachment: either no attachment exists with the given id, or the attachment exists but belongs to a different task than the one in the URL path. The API refuses to serve content for an attachment not linked to the specified task.","triggerScenarios":"GET /runtime/tasks/{taskId}/attachments/{attachmentId}/content where taskService.getAttachment(attachmentId) returns null or attachment.getTaskId() != taskId.","commonSituations":"Copy-pasting an attachmentId from a different task; using a historic attachment id after task cleanup; tenant/datasource mismatch causing lookups against the wrong database; typos in the id.","solutions":["Verify the attachmentId actually belongs to the given taskId (list via GET .../attachments first)","Re-fetch the attachment list for the correct task and use one of those ids","Check you are pointed at the right database/tenant/schema","If the task is historic, use the corresponding history endpoints"],"exampleFix":"// before\nGET /runtime/tasks/101/attachments/999/content   // 999 belongs to task 202\n// after\nGET /runtime/tasks/101/attachments   // pick an id from this list\nGET /runtime/tasks/101/attachments/{correctId}/content","handlingStrategy":"validation","validationCode":"const att = await api.getTaskAttachment(taskId, attachmentId); // 404 here means wrong pair, don't call content\nif (!att) throw new Error(`Attachment ${attachmentId} does not belong to task ${taskId}`);","typeGuard":null,"tryCatchPattern":"try { return await api.getAttachmentContent(taskId, attachmentId); } catch (e) { if (e.status === 404) { return null; // treat as missing\n} throw e; }","preventionTips":["Always obtain attachmentId from the parent task's attachment list","Never hard-code attachment ids across environments","Re-fetch ids after task completion/cleanup"],"tags":["rest","not-found","attachments","id-mismatch"],"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"}