Tencent/matrix · error · IllegalArgumentException

Cannot add the same observer with different lifecycles

Error message

Cannot add the same observer with different lifecycles

What it means

StatefulOwner.checkLifecycle validates that an observer previously registered with one lifecycle owner is not re-registered against a different one. If the same observer instance claims attachment to a different LifecycleOwner (per isAttachedTo) or violates the wrapper-type expectations, it throws IllegalArgumentException to prevent ambiguous lifecycle binding.

Solutions

  1. Create a new observer instance for each LifecycleOwner instead of sharing one observer across lifecycles.
  2. Register the observer with the same LifecycleOwner it was originally attached to.
  3. Remove (unobserve) the observer from its previous owner before re-registering with another one.

Example fix

// before
val shared = object : IStateObserver { ... }
activityALifecycleOwner.let { owner.observeForever(it, shared) }
activityBLifecycleOwner.let { owner.observeForever(it, shared) } // throws
// after
activityBLifecycleOwner.let { owner.observeForever(it, object : IStateObserver { ... }) }
Defensive patterns

Strategy: type-guard

Validate before calling

// Track registration per owner
private val registered = mutableMapOf<IStateObserver, LifecycleOwner>()
fun safeObserve(owner: IStatefulOwner, lc: LifecycleOwner, obs: IStateObserver) {
    check(registered[obs] == null || registered[obs] === lc) { "observer bound to different lifecycle" }
    owner.observeForever(lc, obs); registered[obs] = lc
}

Type guard

fun isSameLifecycle(prev: LifecycleOwner?, cur: LifecycleOwner) = prev == null || prev === cur

Try / catch

try {
    owner.observeForever(lifecycleOwner, observer)
} catch (e: IllegalArgumentException) {
    MatrixLog.w(TAG, "observer already bound to another lifecycle", e)
}

Prevention

When it happens

Trigger: Calling observe/observeForever with the same observer instance but a different LifecycleOwner than the one it was first registered with; checkLifecycle detects the mismatch and throws.

Common situations: Reusing a single IStateObserver object across multiple Activities/Fragments instead of creating a new observer per lifecycle; passing an observer registered on a ProcessLifecycleOwner into an Activity-scoped registration.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/b18e79105ee75389. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/lifecycle/StatefulOwner.kt:120

    }

    override fun isAttachedTo(owner: LifecycleOwner?) = lifecycleOwner == owner

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun release() {
        statefulOwner.removeObserver(observer)
    }
}

private fun ObserverWrapper.checkLifecycle(lifecycleOwner: LifecycleOwner?): Boolean {
    val broken = if (lifecycleOwner != null) {
        isAttachedTo(lifecycleOwner)
    } else {
        this is AutoReleaseObserverWrapper
    }

    if (broken) {
        throw IllegalArgumentException(
            "Cannot add the same observer with different lifecycles"
        )
    }

    return false
}

private enum class State(val dispatch: ((observer: IStateObserver) -> Unit)?) {
    INIT(null),
    ON({ observer -> observer.on() }),
    OFF({ observer -> observer.off() });
}

open class StatefulOwner(val async: Boolean = true) : IStatefulOwner {

    @Volatile
    private var state = State.INIT

View on GitHub (pinned to 3b8293bd65)