flowable/flowable-engine · error · FlowableObjectNotFoundException

Task ' ' does not have an attachment with id ' '.

Error message

Task '{taskId}' does not have an attachment with id '{attachmentId}'.

What it means

FlowableObjectNotFoundException thrown when reading a single task attachment: no attachment exists with the given id, or it belongs to a different task than the path parameter. Note it declares Comment.class as the entity class — a copy-paste artifact — but the semantics are attachment-not-found.

Solutions

  1. List the task's attachments (GET .../attachments) and use a returned id
  2. Confirm the taskId/attachmentId pair from the original creation response
  3. Point the client at the correct database/tenant
  4. Check the attachment was not deleted by another process

Example fix

// before
GET /runtime/tasks/101/attachments/wrong-id
// after
GET /runtime/tasks/101/attachments  // then
GET /runtime/tasks/101/attachments/{id-from-list}
Defensive patterns

Strategy: validation

Validate before calling

const list = await api.getTaskAttachments(taskId);
if (!list.some(a => a.id === attachmentId)) throw new Error('attachmentId not in task attachment list');

Try / catch

try { return await api.getTaskAttachment(taskId, attachmentId); } catch (e) { if (e.status === 404) return null; throw e; }

Prevention

When it happens

Trigger: GET /runtime/tasks/{taskId}/attachments/{attachmentId} where taskService.getAttachment(attachmentId) is null or the attachment's taskId differs from the path taskId.

Common situations: Attachment id from another task or process; attachment deleted earlier; wrong REST deployment/tenant pointing at another database; stale cached ids in client code.

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/0b90d32a49bb666f. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/task/TaskAttachmentResource.java:55

 * @author Frederik Heremans
 */
@RestController
@Api(tags = { "Task Attachments" }, authorizations = { @Authorization(value = "basicAuth") })
public class TaskAttachmentResource extends TaskBaseResource {

    @ApiOperation(value = "Get an attachment on a task", tags = { "Task Attachments" })
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "Indicates the task and attachment were found and the attachment is returned."),
            @ApiResponse(code = 404, message = "Indicates the requested task was not found or the tasks does not have a attachment with the given ID.")
    })
    @GetMapping(value = "/runtime/tasks/{taskId}/attachments/{attachmentId}", produces = "application/json")
    public AttachmentResponse getAttachment(@ApiParam(name = "taskId") @PathVariable("taskId") String taskId, @ApiParam(name = "attachmentId") @PathVariable("attachmentId") String attachmentId) {

        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 + "'.", Comment.class);
        }

        return restResponseFactory.createAttachmentResponse(attachment);
    }

    @ApiOperation(value = "Delete an attachment on a task", tags = { "Task Attachments"}, code = 204)
    @ApiResponses(value = {
            @ApiResponse(code = 204, message = "Indicates the task and attachment were found and the attachment is deleted. Response body is left empty intentionally."),
            @ApiResponse(code = 404, message = "Indicates the requested task was not found or the tasks does not have a attachment with the given ID.")
    })
    @DeleteMapping(value = "/runtime/tasks/{taskId}/attachments/{attachmentId}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteAttachment(@ApiParam(name = "taskId") @PathVariable("taskId") String taskId, @ApiParam(name = "attachmentId") @PathVariable("attachmentId") String attachmentId) {

        Task task = getTaskFromRequestWithoutAccessCheck(taskId);

        Attachment attachment = taskService.getAttachment(attachmentId);
        if (attachment == null || !task.getId().equals(attachment.getTaskId())) {

View on GitHub (pinned to d6d39ce1c6)