{"record":{"id":"a9629a24a01a5379","repo":"prestodb/presto","slug":"s-header-is-not-set-on-an-encrypted-object-s","errorCode":null,"errorMessage":"%s header is not set on an encrypted object: %s","messagePattern":"(.+?) header is not set on an encrypted object: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3FileSystem.java","lineNumber":414,"sourceCode":"        }\n        catch (IllegalArgumentException e) {\n            log.debug(e, \"Failed to parse contentType [%s], assuming not a directory\", objectMetadata.getContentType());\n            return false;\n        }\n\n        return mediaType.is(X_DIRECTORY_MEDIA_TYPE) ||\n                (mediaType.is(OCTET_STREAM_MEDIA_TYPE)\n                        && metadata.isKeyNeedsPathSeparator()\n                        && objectMetadata.getContentLength() == 0);\n    }\n\n    private static long getObjectSize(Path path, ObjectMetadata metadata)\n            throws IOException\n    {\n        Map<String, String> userMetadata = metadata.getUserMetadata();\n        String length = userMetadata.get(UNENCRYPTED_CONTENT_LENGTH);\n        if (userMetadata.containsKey(SERVER_SIDE_ENCRYPTION) && length == null) {\n            throw new IOException(format(\"%s header is not set on an encrypted object: %s\", UNENCRYPTED_CONTENT_LENGTH, path));\n        }\n        return (length != null) ? Long.parseLong(length) : metadata.getContentLength();\n    }\n\n    @Override\n    public FSDataInputStream open(Path path, int bufferSize)\n    {\n        return new FSDataInputStream(\n                new BufferedFSInputStream(\n                        new PrestoS3InputStream(s3, getBucketName(uri), path, maxAttempts, maxBackoffTime, maxRetryTime),\n                        bufferSize));\n    }\n\n    @Override\n    public FSDataOutputStream create(Path path, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress)\n            throws IOException\n    {\n        if ((!overwrite) && exists(path)) {","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3FileSystem.java#L396-L432","documentation":"getObjectSize throws IOException when an object carries the SSE customer-key user-metadata (server-side encryption header) but lacks the Unencrypted-Content-Length header. With such encryption the stored content length is not the plaintext size, so Presto cannot determine the file size without that recorded header.","triggerScenarios":"Reading a file that was uploaded with SSE-C client-side encryption headers by another tool that did not set the x-amz-meta-unencrypted-content-length user metadata, then calling getFileStatus/open on it.","commonSituations":"Files migrated from older Hadoop-S3 clients using s3a SSE-C settings; external uploads encrypted with SSE-C without emulating the Hadoop metadata convention; bucket copy operations that drop user metadata.","solutions":["Re-upload the object with the UNENCRYPTED_CONTENT_LENGTH user metadata set to the plaintext size.","Remove SSE-C encryption or re-encrypt server-side (SSE-S3/KMS) so content length is directly readable.","Copy the object with metadata preservation enabled (aws s3 cp --metadata).","Verify with aws s3api head-object that the x-amz-meta-unencrypted-content-length header is present."],"exampleFix":"// before (uploaded encrypted object without length header)\nPutObjectRequest req = new PutObjectRequest(bucket, key, file);\nreq.setSseAwsKeyManagementId(...); // or SSE-C without user metadata\n// after\nObjectMetadata md = new ObjectMetadata();\nmd.setUserMetadata(Collections.singletonMap(\"X-Amz-Meta-Unencrypted-Content-Length\", String.valueOf(file.length())));\nreq.setMetadata(md);","handlingStrategy":"validation","validationCode":"ObjectMetadata md = s3.getObjectMetadata(bucket, key);\nboolean sseC = md.getUserMetadata().containsKey(\"amazon-server-side-encryption\") ||\n              md.getSSEAlgorithm() == null && md.getUserMetadata().keySet().stream().anyMatch(k -> k.toLowerCase().endsWith(\"server-side-encryption\"));\nif (sseC && md.getUserMetadata().get(\"X-Amz-Meta-Unencrypted-Content-Length\") == null) {\n    throw new IllegalStateException(\"encrypted object missing length header: \" + key);\n}","typeGuard":"boolean isReadableEncryptedObject(ObjectMetadata md) {\n    Map<String,String> um = md.getUserMetadata();\n    return !um.containsKey(\"X-Amz-Meta-Server-Side-Encryption\") || um.get(\"X-Amz-Meta-Unencrypted-Content-Length\") != null;\n}","tryCatchPattern":"try {\n    FileStatus st = fs.getFileStatus(path);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"header is not set\")) {\n        // re-upload with unencrypted-content-length metadata or without SSE-C\n    }\n    throw e;\n}","preventionTips":["When encrypting with SSE-C, always set the unencrypted-content-length user metadata.","Avoid mixing SSE-C-encrypted external files with Hive/S3-backed tables.","Standardize on SSE-S3/SSE-KMS which preserves plain content length.","Audit objects with head-object for the required header before ingestion."],"tags":["s3","encryption","sse-c","metadata"],"backgroundTag":"missing-metadata-header","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}