{"record":{"id":"0998d24f8566091a","repo":"conductor-oss/conductor","slug":"error-downloading-from-s3-path-s","errorCode":null,"errorMessage":"Error downloading from S3 - path:%s","messagePattern":"Error downloading from S3 - path:(.+?)","errorType":"exception","errorClass":"TransientException","httpStatus":null,"severity":"error","filePath":"awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java","lineNumber":188,"sourceCode":"\n    /**\n     * Downloads the payload stored in the s3 object.\n     *\n     * @param path the S3 key of the object\n     * @return an input stream containing the contents of the object Caller is expected to close the\n     *     input stream.\n     */\n    @Override\n    public InputStream download(String path) {\n        try {\n            GetObjectRequest request =\n                    GetObjectRequest.builder().bucket(bucketName).key(path).build();\n\n            return s3Client.getObject(request);\n        } catch (SdkException e) {\n            String msg = String.format(\"Error downloading from S3 - path:%s\", path);\n            LOGGER.error(msg, e);\n            throw new TransientException(msg, e);\n        }\n    }\n\n    private String getObjectKey(PayloadType payloadType) {\n        StringBuilder stringBuilder = new StringBuilder();\n        switch (payloadType) {\n            case WORKFLOW_INPUT:\n                stringBuilder.append(\"workflow/input/\");\n                break;\n            case WORKFLOW_OUTPUT:\n                stringBuilder.append(\"workflow/output/\");\n                break;\n            case TASK_INPUT:\n                stringBuilder.append(\"task/input/\");\n                break;\n            case TASK_OUTPUT:\n                stringBuilder.append(\"task/output/\");\n                break;","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/awss3-storage/src/main/java/com/netflix/conductor/s3/storage/S3PayloadStorage.java#L170-L206","documentation":"Thrown as TransientException (wrapping SdkException) by S3PayloadStorage.download when s3Client.getObject fails. As with upload, SdkException-class errors are treated as retriable. Note: a 'key not found' (NoSuchKey/S3Exception 404) is also an SdkException subclass and would surface here, so callers must distinguish missing objects from transient failures via the chained cause.","triggerScenarios":"Network error, throttling, credential expiry, IAM denial on s3:GetObject, or the requested object key not existing in the bucket. Caught in download's SdkException handler.","commonSituations":"Attempting to download a payload whose upload failed or was pruned; missing s3:GetObject permission; transient network; lifecycle policy deleted the object.","solutions":["Inspect the chained SdkException: a NoSuchKey/404 means the object is genuinely absent (do not retry); other SdkExceptions are retriable.","Retry with backoff for non-404 SdkExceptions.","Grant s3:GetObject and verify the object key exists (it should match the path returned by getLocation at upload time).","Check bucket lifecycle rules are not expiring payloads prematurely."],"exampleFix":"// before\nInputStream in = storage.download(path); // may throw TransientException\n// after\ntry {\n    InputStream in = storage.download(path);\n} catch (TransientException te) {\n    if (te.getCause() instanceof NoSuchKey) {\n        // object genuinely missing — handle absence, do not retry\n    } else {\n        RetryUtils.retryOn(TransientException.class, 5, Duration.ofMillis(300),\n            () -> storage.download(path));\n    }\n}","handlingStrategy":"try-catch","validationCode":"import org.apache.commons.lang3.StringUtils;\nif (StringUtils.isBlank(path)) throw new IllegalArgumentException(\"download path is required\");\n// Best-effort existence check (note: adds a HEAD call)\n// s3Client.headObject(h -> h.bucket(bucketName).key(path));","typeGuard":null,"tryCatchPattern":"import software.amazon.awssdk.services.s3.model.NoSuchKey;\nimport software.amazon.awssdk.services.s3.model.S3Exception;\ntry {\n    return storage.download(path);\n} catch (TransientException te) {\n    Throwable c = te.getCause();\n    if (c instanceof S3Exception s3 && s3.statusCode() == 404) {\n        // object genuinely missing — handle absence, do NOT retry\n        throw new IllegalStateException(\"S3 object not found: \" + path, te);\n    }\n    // otherwise retriable — bounded backoff retry\n    throw te;\n}","preventionTips":["Distinguish 404/NoSuchKey (missing) from other SdkExceptions (retriable) via the chained cause.","Grant s3:GetObject on the bucket.","Verify the path matches what getLocation returned at upload time.","Check bucket lifecycle rules are not deleting payloads early."],"tags":["s3","aws","network","transient","storage","iam"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}