apache/seatunnel · error · org.apache.seatunnel.api.configuration.util.OptionValidationException

Condition for option '%s' has a null operator

Error message

Condition for option '%s' has a null operator

What it means

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.

Source

Thrown at seatunnel-api/src/main/java/org/apache/seatunnel/api/configuration/util/ConditionEvaluators.java:46

import java.util.Objects;

/**
 * Registry of per-{@link ConditionOperator} evaluation logic. Stateless; every evaluator is a pure
 * function of (value, condition, config).
 */
public final class ConditionEvaluators {

    @FunctionalInterface
    interface Evaluator {
        boolean evaluate(Object value, Condition<?> condition, ReadonlyConfig config);
    }

    private static final Map<ConditionOperator, Evaluator> REGISTRY = createRegistry();

    static boolean evaluate(Condition<?> condition, ReadonlyConfig config) {
        ConditionOperator operator = condition.getOperator();
        if (operator == null) {
            throw new OptionValidationException(
                    "Condition for option '%s' has a null operator", condition.getOption().key());
        }

        Object value = config.get(condition.getOption());
        Evaluator evaluator = REGISTRY.get(operator);
        return evaluator.evaluate(value, condition, config);
    }

    @SuppressWarnings({"rawtypes"})
    private static Map<ConditionOperator, Evaluator> createRegistry() {
        Map<ConditionOperator, Evaluator> m = new EnumMap<>(ConditionOperator.class);

        // Equality
        m.put(ConditionOperator.EQUAL, (v, c, cfg) -> Objects.equals(c.getExpectValue(), v));
        m.put(ConditionOperator.NOT_EQUAL, (v, c, cfg) -> !Objects.equals(c.getExpectValue(), v));

        // Numeric (null value -> false, preserving or() short-circuit)
        m.put(

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Inspect the wrapped cause to identify which directory failed and why (NotFound, PermissionDenied, timeout).
  2. Ensure the SeaTunnel worker's credentials have read+execute (list) permission on every directory under the root, not just the root itself.
  3. Freeze concurrent modifications (deletes/renames) of the source tree while the job lists files, or point the scan at a stable snapshot path.
  4. Retry the job if the cause is transient (S3 throttling/network); consider fewer, larger directories to reduce listing pressure.

Example fix

// before
hdfs dfs -chmod 700 /data/source/private_dir   // worker can't list it
// after
hdfs dfs -chmod 755 /data/source/private_dir   # or grant the job user read+execute
Defensive patterns

Strategy: retry

Validate before calling

java
// Pre-walk the tree and verify list permission on every directory
Files.walkFileTree(localRoot, new SimpleFileVisitor<>() {
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        if (!dir.toFile().canRead()) {
            throw new IllegalStateException("No list permission on directory: " + dir);
        }
        return FileVisitResult.CONTINUE;
    }
});

Try / catch

java
try {
    scanner.scan(session, root, filter, consumer);
} catch (IOException e) {
    if (e.getMessage().startsWith("Failed during source_listing for protocol=")) {
        if (isTransient(e.getCause())) { /* S3 throttling, timeouts */ retryScan(); }
        else LOG.error("Directory listing failed (check per-directory permissions, concurrent deletes): "
            + e.getMessage(), e.getCause());
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/0e5a3ac344797e35. Report an issue: GitHub.