{"record":{"id":"0e5a3ac344797e35","repo":"apache/seatunnel","slug":"condition-for-option-s-has-a-null-operator","errorCode":null,"errorMessage":"Condition for option '%s' has a null operator","messagePattern":"Condition for option '(.+?)' has a null operator","errorType":"validation","errorClass":"org.apache.seatunnel.api.configuration.util.OptionValidationException","httpStatus":null,"severity":"error","filePath":"seatunnel-api/src/main/java/org/apache/seatunnel/api/configuration/util/ConditionEvaluators.java","lineNumber":46,"sourceCode":"import java.util.Objects;\n\n/**\n * Registry of per-{@link ConditionOperator} evaluation logic. Stateless; every evaluator is a pure\n * function of (value, condition, config).\n */\npublic final class ConditionEvaluators {\n\n    @FunctionalInterface\n    interface Evaluator {\n        boolean evaluate(Object value, Condition<?> condition, ReadonlyConfig config);\n    }\n\n    private static final Map<ConditionOperator, Evaluator> REGISTRY = createRegistry();\n\n    static boolean evaluate(Condition<?> condition, ReadonlyConfig config) {\n        ConditionOperator operator = condition.getOperator();\n        if (operator == null) {\n            throw new OptionValidationException(\n                    \"Condition for option '%s' has a null operator\", condition.getOption().key());\n        }\n\n        Object value = config.get(condition.getOption());\n        Evaluator evaluator = REGISTRY.get(operator);\n        return evaluator.evaluate(value, condition, config);\n    }\n\n    @SuppressWarnings({\"rawtypes\"})\n    private static Map<ConditionOperator, Evaluator> createRegistry() {\n        Map<ConditionOperator, Evaluator> m = new EnumMap<>(ConditionOperator.class);\n\n        // Equality\n        m.put(ConditionOperator.EQUAL, (v, c, cfg) -> Objects.equals(c.getExpectValue(), v));\n        m.put(ConditionOperator.NOT_EQUAL, (v, c, cfg) -> !Objects.equals(c.getExpectValue(), v));\n\n        // Numeric (null value -> false, preserving or() short-circuit)\n        m.put(","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-api/src/main/java/org/apache/seatunnel/api/configuration/util/ConditionEvaluators.java#L28-L64","documentation":"FileDiscoveryScanner.scan walks candidate directories and lists their contents; when the per-directory listing (e.g. session.listStatus) throws IOException, scan rethrows it wrapped as \"Failed during source_listing for protocol=..., path=...\" with the directory path masked and the original exception as cause. Unlike the root_stat error, this happens after the root was successfully statted, i.e. during traversal of a subdirectory.","triggerScenarios":"Calling scan where listing a traversed directory throws IOException — the directory was deleted mid-traversal, permission denied on a subdirectory, transient storage timeouts (S3 throttling, HDFS NameNode retries exhausted), or a listStatus failure on a specific child directory.","commonSituations":"Concurrent jobs/users deleting or moving directories during a scan; per-subdirectory permissions differing from the root; S3 rate limiting under large directory trees; flaky network to the storage backend.","solutions":["Inspect the wrapped cause to identify which directory failed and why (NotFound, PermissionDenied, timeout).","Ensure the SeaTunnel worker's credentials have read+execute (list) permission on every directory under the root, not just the root itself.","Freeze concurrent modifications (deletes/renames) of the source tree while the job lists files, or point the scan at a stable snapshot path.","Retry the job if the cause is transient (S3 throttling/network); consider fewer, larger directories to reduce listing pressure."],"exampleFix":"// before\nhdfs dfs -chmod 700 /data/source/private_dir   // worker can't list it\n// after\nhdfs dfs -chmod 755 /data/source/private_dir   # or grant the job user read+execute","handlingStrategy":"retry","validationCode":"java\n// Pre-walk the tree and verify list permission on every directory\nFiles.walkFileTree(localRoot, new SimpleFileVisitor<>() {\n    @Override\n    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {\n        if (!dir.toFile().canRead()) {\n            throw new IllegalStateException(\"No list permission on directory: \" + dir);\n        }\n        return FileVisitResult.CONTINUE;\n    }\n});","typeGuard":null,"tryCatchPattern":"java\ntry {\n    scanner.scan(session, root, filter, consumer);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Failed during source_listing for protocol=\")) {\n        if (isTransient(e.getCause())) { /* S3 throttling, timeouts */ retryScan(); }\n        else LOG.error(\"Directory listing failed (check per-directory permissions, concurrent deletes): \"\n            + e.getMessage(), e.getCause());\n    }\n}","preventionTips":["Grant the job user list permission on every subdirectory under the source root","Avoid deleting/moving source directories while a job is listing files","Enable storage client retries (S3 SDK, HDFS client) for transient throttling","Keep source trees shallow; inspect e.getCause() for the failing directory"],"tags":["filesystem","io","file-discovery","hadoop"],"backgroundTag":"file-read-failed","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}