ReactiveX/RxJava · error · IllegalArgumentException

errors is empty

Error message

errors is empty

What it means

Thrown by the CompositeException constructor when, after flattening nested CompositeExceptions, replacing null elements with NullPointerException, and de-duplicating, the resulting exception list is empty. This is a programming error: a CompositeException must wrap at least one real cause. An empty input collection, or a collection containing only nulls/empty nested composites, triggers it.

Source

Thrown at src/main/java/io/reactivex/rxjava4/exceptions/CompositeException.java:78

     *
     * @throws IllegalArgumentException if <code>errors</code> is empty.
     */
    public CompositeException(@NonNull Iterable<? extends Throwable> errors) {
        Set<Throwable> deDupedExceptions = new LinkedHashSet<>();
        if (errors != null) {
            for (Throwable ex : errors) {
                if (ex instanceof CompositeException ce) {
                    deDupedExceptions.addAll(ce.getExceptions());
                } else {
                    deDupedExceptions.add(Objects.requireNonNullElseGet(ex,
                            () -> new NullPointerException("Throwable was null!")));
                }
            }
        } else {
            deDupedExceptions.add(new NullPointerException("errors was null"));
        }
        if (deDupedExceptions.isEmpty()) {
            throw new IllegalArgumentException("errors is empty");
        }
        List<Throwable> localExceptions = new ArrayList<>(deDupedExceptions);
        this.exceptions = Collections.unmodifiableList(localExceptions);
        this.message = exceptions.size() + " exceptions occurred. ";
    }

    /**
     * Retrieves the list of exceptions that make up the {@code CompositeException}.
     *
     * @return the exceptions that make up the {@code CompositeException}, as a {@link List} of {@link Throwable}s
     */
    @NonNull
    public List<Throwable> getExceptions() {
        return exceptions;
    }

    @Override
    @NonNull

View on GitHub (pinned to a8ab535614)

Solutions

  1. Before constructing a CompositeException, check that the list is non-empty and contains at least one non-null Throwable.
  2. If the aggregation legitimately produced no errors, skip creating the CompositeException entirely (return success instead).
  3. Filter nulls out of the list and verify size > 0 before passing it in.
  4. Add a test: an empty list throws, a single-element list succeeds, a list of nulls becomes a single NullPointerException-wrapped cause (non-empty, so no throw).

Example fix

// before
List<Throwable> errs = collectErrors(tasks);
throw new CompositeException(errs); // throws 'errors is empty' if errs is empty

// after
List<Throwable> errs = collectErrors(tasks);
if (errs.isEmpty()) {
    return Result.success();
}
throw new CompositeException(errs);
Defensive patterns

Strategy: validation

Validate before calling

void throwIfAny(List<Throwable> errors) {
    List<Throwable> real = errors.stream()
        .filter(Objects::nonNull)
        .toList();
    if (real.isEmpty()) return; // nothing to wrap
    throw new CompositeException(real);
}

Try / catch

try {
    throw new CompositeException(errors);
} catch (IllegalArgumentException e) {
    // 'errors is empty' -> there were no errors to report
}

Prevention

When it happens

Trigger: new CompositeException(emptyList()); new CompositeException(listOfNulls); passing a List that contains only a CompositeException whose own exceptions list is empty; building a composite from a stream/collect that filtered everything out.

Common situations: Aggregating errors from a fan-out operation where zero sub-tasks failed (the 'nothing went wrong' case is being incorrectly wrapped); collecting exceptions into a list inside a catch-all loop that may never append; defensive code that always wraps a (possibly empty) list.

Related errors


AI-assisted analysis of ReactiveX/RxJava@a8ab535614 (2026-08-13). Data as JSON: /api/errors/cc086dd83b36e0ba. Report an issue: GitHub.