flowable/flowable-engine · error · FlowableObjectNotFoundException
Attachment with id ' ' does not have content associated…
Error message
Attachment with id '{attachmentId}' does not have content associated with it. What it means
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.
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
Example fix
// before
POST /runtime/tasks/{id}/attachments {"name":"x","url":"http://..."} // metadata only
GET /runtime/tasks/{id}/attachments/{attId}/content // 404 no content
// after
POST /runtime/tasks/{id}/attachments -F 'file=@doc.pdf' // binary upload
GET /runtime/tasks/{id}/attachments/{attId}/content Defensive patterns
Strategy: fallback
Validate before calling
const att = await api.getTaskAttachment(taskId, attachmentId);
if (att.url) { window.open(att.url); return; } // metadata-only attachment has externalUrl Type guard
function hasExternalContent(att) { return typeof att.url === 'string' && att.url.length > 0; } Try / catch
try { return await api.getAttachmentContent(taskId, attachmentId); } catch (e) { if (e.status === 404) { window.open(att.url || '/no-content'); return null; } throw e; } Prevention
- 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
When it happens
Trigger: GET .../attachments/{attachmentId}/content on an attachment created without content (metadata/url-only attachment), or whose stored content was removed by cleanup/archival.
Common situations: 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.
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
- Error creating attachment data
- Task ' ' does not have an attachment with id ' '.
- Task ' ' does not have an attachment with id ' '.
- Attachment content is required.
- Could not find a app model json with id
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/69b601666035dcb8.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskAttachmentContentResource.java:65
@ApiOperation(value = "Get the content for an attachment", tags = { "Task Attachments" },
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.")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Indicates the task and attachment was found and the requested content is returned."),
@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.")
})
@GetMapping(value = "/runtime/tasks/{taskId}/attachments/{attachmentId}/content")
public ResponseEntity<byte[]> getAttachmentContent(@ApiParam(name = "taskId") @PathVariable("taskId") String taskId, @ApiParam(name = "attachmentId") @PathVariable("attachmentId") String attachmentId, HttpServletResponse response) {
HistoricTaskInstance task = getHistoricTaskFromRequest(taskId);
Attachment attachment = taskService.getAttachment(attachmentId);
if (attachment == null || !task.getId().equals(attachment.getTaskId())) {
throw new FlowableObjectNotFoundException("Task '" + task.getId() + "' does not have an attachment with id '" + attachmentId + "'.", Attachment.class);
}
InputStream attachmentStream = taskService.getAttachmentContent(attachmentId);
if (attachmentStream == null) {
throw new FlowableObjectNotFoundException("Attachment with id '" + attachmentId + "' does not have content associated with it.", Attachment.class);
}
HttpHeaders responseHeaders = new HttpHeaders();
MediaType mediaType = null;
if (attachment.getType() != null) {
try {
mediaType = MediaType.valueOf(attachment.getType());
responseHeaders.set("Content-Type", attachment.getType());
} catch (Exception e) {
// ignore if unknown media type
}
}
if (mediaType == null) {
responseHeaders.set("Content-Type", "application/octet-stream");
}
try {View on GitHub (pinned to d6d39ce1c6)