Tencent/tinker · error · IllegalStateException

target is null.

Error message

target is null.

What it means

ServiceBinderInterceptor.decorate() wraps the IBinder fetched from ServiceManager.getService(serviceName) in a proxy. It throws IllegalStateException('target is null.') when that lookup returned null — the named system service is not currently registered, so there is nothing to intercept.

Source

Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor.java:69

    public ServiceBinderInterceptor(Context context, String serviceName, BinderInvocationHandler binderInvocationHandler) {
        while (context != null && context instanceof ContextWrapper) {
            context = ((ContextWrapper) context).getBaseContext();
        }
        mBaseContext = context;
        mServiceName = serviceName;
        mBinderInvocationHandler = binderInvocationHandler;
    }

    @Override
    protected IBinder fetchTarget() throws Throwable {
        return (IBinder) sGetServiceMethod.invoke(null, mServiceName);
    }

    @Override
    protected IBinder decorate(IBinder target) throws Throwable {
        if (target == null) {
            throw new IllegalStateException("target is null.");
        }
        if (ITinkerHotplugProxy.class.isAssignableFrom(target.getClass())) {
            // Already intercepted, just return the target.
            return target;
        } else {
            return createProxy(getAllInterfacesThroughDeriveChain(target.getClass()),
                    new FakeClientBinderHandler(target, mBinderInvocationHandler));
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void inject(IBinder decorated) throws Throwable {
        final Map<String, IBinder> sCache = (Map<String, IBinder>) sSCacheField.get(null);
        sCache.put(mServiceName, decorated);
        if (Context.ACTIVITY_SERVICE.equals(mServiceName)) {
            fixAMSBinderCache(decorated);
        } else if (EnvConsts.PACKAGE_MANAGER_SRVNAME.equals(mServiceName)) {

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Verify the exact service name against ServiceManager.listServices() on the target device.
  2. Defer the interception until the service is registered (retry after onServiceRegistered / slight delay via lifecycle, not tight loop).
  3. Treat a null target as 'nothing to intercept' and skip decoration instead of crashing.

Example fix

// before
IBinder binder = ServiceManager.getService(name); // may be null early

// after
IBinder binder = ServiceManager.getService(name);
if (binder == null) {
    // service not yet published; defer interception
    return;
}
Defensive patterns

Strategy: validation

Validate before calling

IBinder binder = (IBinder) sGetServiceMethod.invoke(null, mServiceName);
if (binder == null) {
    // service not published yet: defer interception, do not decorate
    return;
}

Try / catch

try {
    interceptor.decorate(binder);
} catch (IllegalStateException e) {
    // target null: retry later when the service is registered
}

Prevention

When it happens

Trigger: fetchTarget() invoking ServiceManager.getService for a service that is not yet published (early boot, service restart) or that does not exist on the device (wrong service name, OEM removed it).

Common situations: Intercepting services during Application attachBaseContext before system services are up; typos in the service name; targeting a service absent on the specific ROM.

Related errors


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/c3b8d8b64a6217f6. Report an issue: GitHub.