conductor-oss/conductor · error · NonTransientException

File not found on storage after multipart complete: {}

Error message

File not found on storage after multipart complete: {}

What it means

Thrown by FileStorageServiceImpl.completeMultipartUpload after fileStorage.completeMultipartUpload returns, when getStorageFileInfo reports the assembled object is absent. The multipart completion call returned but the final object is not visible on the backend, so the file cannot be marked UPLOADED. Raised as NonTransientException (not retried).

Source

Thrown at core/src/main/java/org/conductoross/conductor/core/storage/FileStorageServiceImpl.java:206

                        properties.getSignedUrlExpiration());
        long expiresAt = Instant.now().plus(properties.getSignedUrlExpiration()).toEpochMilli();

        FileUploadUrlResponse response = new FileUploadUrlResponse();
        response.setFileHandleId(FileIdToFileHandleIdConverter.toFileHandleId(fileId));
        response.setUploadUrl(url);
        response.setExpiresAt(expiresAt);
        return response;
    }

    @Override
    public FileUploadCompleteResponse completeMultipartUpload(
            String workflowId, String fileId, String uploadId, List<String> partETags) {
        FileModel model = getOwnedFile(workflowId, fileId);
        fileStorage.completeMultipartUpload(model.getStoragePath(), uploadId, partETags);

        StorageFileInfo info = fileStorage.getStorageFileInfo(model.getStoragePath());
        if (info == null || !info.isExists()) {
            throw new NonTransientException(
                    "File not found on storage after multipart complete: " + fileId);
        }

        fileMetadataDAO.updateUploadComplete(
                fileId, FileUploadStatus.UPLOADED, info.getContentHash(), info.getContentSize());

        FileUploadCompleteResponse response = new FileUploadCompleteResponse();
        response.setFileHandleId(FileIdToFileHandleIdConverter.toFileHandleId(fileId));
        response.setUploadStatus(FileUploadStatus.UPLOADED);
        response.setContentHash(info.getContentHash());
        return response;
    }

    @Override
    public void abortMultipartUpload(String workflowId, String fileId, String uploadId) {
        FileModel model = getOwnedFile(workflowId, fileId);
        fileStorage.abortMultipartUpload(model.getStoragePath(), uploadId);
    }

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Verify all part ETags correspond to actual PUT results for this uploadId and are in order.
  2. Ensure every part was uploaded to its presigned URL before completing.
  3. Abort the multipart upload and restart with a fresh uploadId if the assembly is unrecoverable.
  4. Check backend logs/consistency if all inputs are correct.

Example fix

// before - completing with empty/incorrect eTags
fileStorageService.completeMultipartUpload(wfId, fileId, uploadId, List.of()); // throws

// after - pass real eTags from each part PUT
List<String> eTags = partUploads.stream().map(PartResult::getEtag).toList();
fileStorageService.completeMultipartUpload(wfId, fileId, uploadId, eTags);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure all parts uploaded and eTags collected before completing
List<String> eTags = partResults.stream().map(PartResult::getEtag).toList();
if (eTags.isEmpty()) { /* do not complete */ }

Try / catch

try {
    fileStorageService.completeMultipartUpload(wfId, fileId, uploadId, eTags);
} catch (NonTransientException e) {
    // assembled object missing; abort and restart multipart upload
}

Prevention

When it happens

Trigger: Completing a multipart upload with stale/incorrect part ETags; the backend did not assemble the object (e.g. wrong uploadId, missing parts); eventual-consistency window; backend error during assembly that returned success.

Common situations: Client passed part ETags out of order or for the wrong upload; aborted then re-completed; parts were never actually uploaded to their presigned URLs; backend bug returning ok but not materializing the object.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/750a88866e74054b. Report an issue: GitHub.