ReactiveX/RxJava · error · IllegalArgumentException

unsafeCreate(Maybe) should be upgraded

Error message

unsafeCreate(Maybe) should be upgraded

What it means

Thrown by Maybe.unsafeCreate when the argument is already a Maybe instance. unsafeCreate is meant to bridge foreign MaybeSource implementations; wrapping a real Maybe is redundant and almost always a migration remnant. Note the NPE check runs after this guard, so a null Maybe is fine but a non-null Maybe triggers this error.

Source

Thrown at src/main/java/io/reactivex/rxjava4/core/Maybe.java:1533

     * any safeguards by using a callback that is called with a {@link MaybeObserver}.
     * <p>
     * <img width="640" height="262" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Maybe.unsafeCreate.png" alt="">
     * <dl>
     * <dt><b>Scheduler:</b></dt>
     * <dd>{@code unsafeCreate} does not operate by default on a particular {@link Scheduler}.</dd>
     * </dl>
     * @param <T> the value type
     * @param onSubscribe the function that is called with the subscribing {@code MaybeObserver}
     * @return the new {@code Maybe} instance
     * @throws IllegalArgumentException if {@code onSubscribe} is a {@code Maybe}
     * @throws NullPointerException if {@code onSubscribe} is {@code null}
     */
    @CheckReturnValue
    @NonNull
    @SchedulerSupport(SchedulerSupport.NONE)
    public static <@NonNull T> Maybe<T> unsafeCreate(@NonNull MaybeSource<T> onSubscribe) {
        if (onSubscribe instanceof Maybe) {
            throw new IllegalArgumentException("unsafeCreate(Maybe) should be upgraded");
        }
        Objects.requireNonNull(onSubscribe, "onSubscribe is null");
        return RxJavaPlugins.onAssembly(new MaybeUnsafeCreate<>(onSubscribe));
    }

    /**
     * Constructs a {@code Maybe} that creates a dependent resource object which is disposed of when the
     * generated {@link MaybeSource} terminates or the downstream calls dispose().
     * <p>
     * <img width="640" height="378" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Maybe.using.png" alt="">
     * <dl>
     *  <dt><b>Scheduler:</b></dt>
     *  <dd>{@code using} does not operate by default on a particular {@link Scheduler}.</dd>
     * </dl>
     *
     * @param <T> the element type of the generated {@code MaybeSource}
     * @param <D> the type of the resource associated with the output sequence
     * @param resourceSupplier

View on GitHub (pinned to a8ab535614)

Solutions

  1. Use the Maybe directly instead of wrapping: drop unsafeCreate.
  2. If you need to hide identity, call source.hide().
  3. Restrict the call site so unsafeCreate only receives non-Maybe MaybeSource instances.

Example fix

// before
Maybe<String> src = loadMaybe();
Maybe<String> wrapped = Maybe.unsafeCreate(src);

// after
Maybe<String> wrapped = src;
// or:
Maybe<String> hidden = src.hide();
Defensive patterns

Strategy: type-guard

Validate before calling

MaybeSource<T> source = loadSource();
if (source instanceof Maybe) {
    // unsafeCreate would throw; use directly or hide()
    return;
}

Type guard

static <T> boolean needsUnsafeCreate(MaybeSource<T> s) {
    return s != null && !(s instanceof Maybe);
}

Try / catch

try {
    return Maybe.unsafeCreate(source);
} catch (IllegalArgumentException e) {
    return source instanceof Maybe ? (Maybe<T>) source : Maybe.error(e);
}

Prevention

When it happens

Trigger: Calling Maybe.unsafeCreate(someMaybe) where the runtime type is Maybe. Common when an upstream operator or helper returns Maybe and the caller unconditionally wraps it.

Common situations: RxJava 1.x porting where Observable.unsafeCreate patterns were common; generic adapters that wrap any source without checking identity.

Related errors


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