{"record":{"id":"b21135092f998aff","repo":"apache/flink","slug":"directory-not-empty-and-recursive-false","errorCode":null,"errorMessage":"Directory not empty and recursive = false","messagePattern":"Directory not empty and recursive = false","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"flink-filesystems/flink-s3-fs-native/src/main/java/org/apache/flink/fs/s3native/NativeS3FileSystem.java","lineNumber":392,"sourceCode":"\n    @Override\n    public boolean delete(Path path, boolean recursive) throws IOException {\n        checkNotClosed();\n        final String key = NativeS3ObjectOperations.extractKey(path);\n        final S3Client s3Client = clientProvider.getS3Client();\n\n        try {\n            final FileStatus status = getFileStatus(path);\n\n            if (!status.isDir()) {\n                final DeleteObjectRequest request =\n                        DeleteObjectRequest.builder().bucket(bucketName).key(key).build();\n\n                s3Client.deleteObject(request);\n                return true;\n            } else {\n                if (!recursive) {\n                    throw new IOException(\"Directory not empty and recursive = false\");\n                }\n\n                final FileStatus[] contents = listStatus(path);\n                for (FileStatus file : contents) {\n                    delete(file.getPath(), true);\n                }\n\n                return true;\n            }\n        } catch (FileNotFoundException e) {\n            return false;\n        } catch (S3Exception e) {\n            throw new IOException(\"Failed to delete: \" + path, e);\n        }\n    }\n\n    /**\n     * Creates a directory at the specified path.","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-filesystems/flink-s3-fs-native/src/main/java/org/apache/flink/fs/s3native/NativeS3FileSystem.java#L374-L410","documentation":"NativeS3FileSystem.delete throws IOException('Directory not empty and recursive = false') when delete is called on a path that getFileStatus reports as a directory and recursive=false. Note the check fires for ANY directory delete without the recursive flag, even if the directory prefix currently has no children, because directory-ness is inferred from listing and non-recursive directory deletes are simply not supported.","triggerScenarios":"Calling s3Fs.delete(dirPath, false) where dirPath resolves to a directory FileStatus (objects exist under the prefix, or a directory marker exists).","commonSituations":"Cleanup code ported from a POSIX-style filesystem that calls delete(path, false) on directories; FileOutputCommitter-style task cleanup deleting output directories non-recursively; expecting rmdir semantics on S3.","solutions":["Pass recursive=true when deleting directories: s3Fs.delete(dirPath, true).","If you need non-recursive semantics, first listStatus(path) and delete children individually, then handle the directory itself.","Guard the call: only call delete(...,false) when !status.isDir()."],"exampleFix":"// before\nboolean deleted = s3Fs.delete(dirPath, false); // IOException\n\n// after\nFileStatus st = s3Fs.getFileStatus(dirPath);\nboolean deleted = s3Fs.delete(dirPath, /*recursive=*/ st.isDir());","handlingStrategy":"validation","validationCode":"// choose recursive flag from the actual status\nFileStatus st = s3Fs.getFileStatus(path);\nboolean recursive = st.isDir(); // non-recursive delete only valid for files\nboolean ok = s3Fs.delete(path, recursive);","typeGuard":null,"tryCatchPattern":"try {\n    s3Fs.delete(path, false);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"recursive = false\")) {\n        s3Fs.delete(path, true); // retry recursively if that is acceptable\n    } else {\n        throw e;\n    }\n}","preventionTips":["Do not port POSIX rmdir semantics to S3; directories here are prefixes.","Pass recursive=true whenever the path may be a directory.","Delete children explicitly via listStatus when you need selective cleanup."],"tags":["s3","delete","directory","object-store-semantics"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}