ReactiveX/RxJava · error · IllegalArgumentException

unsafeCreate(Single) should be upgraded

Error message

unsafeCreate(Single) should be upgraded

What it means

Thrown by Single.unsafeCreate when the argument is already a Single instance. unsafeCreate exists to bridge foreign SingleSource implementations; wrapping a real Single is redundant, likely a port remnant, and the docs suggest using hide() if you genuinely need to obscure identity. The NPE check runs before this guard, so a null source throws NPE first.

Source

Thrown at src/main/java/io/reactivex/rxjava4/core/Single.java:1383

     * <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 SingleObserver}
     * @return the new {@code Single} instance
     * @throws NullPointerException if {@code onSubscribe} is {@code null}
     * @throws IllegalArgumentException if {@code source} is a subclass of {@code Single}; such
     * instances don't need conversion and is possibly a port remnant from 1.x or one should use {@link #hide()}
     * instead.
     * @since 2.0
     */
    @CheckReturnValue
    @NonNull
    @SchedulerSupport(SchedulerSupport.NONE)
    public static <@NonNull T> Single<T> unsafeCreate(@NonNull SingleSource<T> onSubscribe) {
        Objects.requireNonNull(onSubscribe, "onSubscribe is null");
        if (onSubscribe instanceof Single) {
            throw new IllegalArgumentException("unsafeCreate(Single) should be upgraded");
        }
        return RxJavaPlugins.onAssembly(new SingleFromUnsafeSource<>(onSubscribe));
    }

    /**
     * Allows using and disposing a resource while running a {@link SingleSource} instance generated from
     * that resource (similar to a try-with-resources).
     * <p>
     * <img width="640" height="380" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/Single.using.v3.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 value type of the {@code SingleSource} generated
     * @param <U> the resource type
     * @param resourceSupplier the {@link Supplier} called for each {@link SingleObserver} to generate a resource object
     * @param sourceSupplier the function called with the returned resource
     *                  object from {@code resourceSupplier} and should return a {@code SingleSource} instance

View on GitHub (pinned to a8ab535614)

Solutions

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

Example fix

// before
Single<String> src = loadSingle();
Single<String> wrapped = Single.unsafeCreate(src);

// after
Single<String> wrapped = src;
// or, to hide identity:
Single<String> hidden = src.hide();
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Calling Single.unsafeCreate(someSingle) where the runtime type is Single. Common when an upstream helper returns Single and the caller wraps it unconditionally.

Common situations: RxJava 1.x-to-3.x/4.x migration; generic adapters that wrap any source without checking its concrete type.

Related errors


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