{"record":{"id":"750a88866e74054b","repo":"conductor-oss/conductor","slug":"file-not-found-on-storage-after-multipart-complete","errorCode":null,"errorMessage":"File not found on storage after multipart complete: {}","messagePattern":"File not found on storage after multipart complete: (.+?)","errorType":"exception","errorClass":"NonTransientException","httpStatus":500,"severity":"error","filePath":"core/src/main/java/org/conductoross/conductor/core/storage/FileStorageServiceImpl.java","lineNumber":206,"sourceCode":"                        properties.getSignedUrlExpiration());\n        long expiresAt = Instant.now().plus(properties.getSignedUrlExpiration()).toEpochMilli();\n\n        FileUploadUrlResponse response = new FileUploadUrlResponse();\n        response.setFileHandleId(FileIdToFileHandleIdConverter.toFileHandleId(fileId));\n        response.setUploadUrl(url);\n        response.setExpiresAt(expiresAt);\n        return response;\n    }\n\n    @Override\n    public FileUploadCompleteResponse completeMultipartUpload(\n            String workflowId, String fileId, String uploadId, List<String> partETags) {\n        FileModel model = getOwnedFile(workflowId, fileId);\n        fileStorage.completeMultipartUpload(model.getStoragePath(), uploadId, partETags);\n\n        StorageFileInfo info = fileStorage.getStorageFileInfo(model.getStoragePath());\n        if (info == null || !info.isExists()) {\n            throw new NonTransientException(\n                    \"File not found on storage after multipart complete: \" + fileId);\n        }\n\n        fileMetadataDAO.updateUploadComplete(\n                fileId, FileUploadStatus.UPLOADED, info.getContentHash(), info.getContentSize());\n\n        FileUploadCompleteResponse response = new FileUploadCompleteResponse();\n        response.setFileHandleId(FileIdToFileHandleIdConverter.toFileHandleId(fileId));\n        response.setUploadStatus(FileUploadStatus.UPLOADED);\n        response.setContentHash(info.getContentHash());\n        return response;\n    }\n\n    @Override\n    public void abortMultipartUpload(String workflowId, String fileId, String uploadId) {\n        FileModel model = getOwnedFile(workflowId, fileId);\n        fileStorage.abortMultipartUpload(model.getStoragePath(), uploadId);\n    }","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/core/src/main/java/org/conductoross/conductor/core/storage/FileStorageServiceImpl.java#L188-L224","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify all part ETags correspond to actual PUT results for this uploadId and are in order.","Ensure every part was uploaded to its presigned URL before completing.","Abort the multipart upload and restart with a fresh uploadId if the assembly is unrecoverable.","Check backend logs/consistency if all inputs are correct."],"exampleFix":"// before - completing with empty/incorrect eTags\nfileStorageService.completeMultipartUpload(wfId, fileId, uploadId, List.of()); // throws\n\n// after - pass real eTags from each part PUT\nList<String> eTags = partUploads.stream().map(PartResult::getEtag).toList();\nfileStorageService.completeMultipartUpload(wfId, fileId, uploadId, eTags);","handlingStrategy":"validation","validationCode":"// Ensure all parts uploaded and eTags collected before completing\nList<String> eTags = partResults.stream().map(PartResult::getEtag).toList();\nif (eTags.isEmpty()) { /* do not complete */ }","typeGuard":null,"tryCatchPattern":"try {\n    fileStorageService.completeMultipartUpload(wfId, fileId, uploadId, eTags);\n} catch (NonTransientException e) {\n    // assembled object missing; abort and restart multipart upload\n}","preventionTips":["Collect real ETags from each part PUT","Verify uploadId and part numbers match","Abort and restart if assembly is unrecoverable"],"tags":["file-storage","multipart-upload","backend","conductor","java"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}