Tencent/matrix · error · IllegalStateException

call forbidden

Error message

call forbidden

What it means

DispatcherStateOwner.syncStates() is an internal API meant to be invoked only by the ProcessSupervisor to push owner states to subordinate processes. It throws IllegalStateException("call forbidden") when called from any process that is not the supervisor, to prevent app code from corrupting supervisor-driven state synchronization.

Solutions

  1. Do not call syncStates from app code; it is supervisor-internal. Use the public owner APIs or let the supervisor drive synchronization.
  2. Guard the call site with `if (ProcessSupervisor.isSupervisor)` before invoking.
  3. If subordinate processes need updated state, trigger it via the supervisor (e.g. scene dispatch from the supervisor side).
  4. Catch IllegalStateException as a safety net when probing process roles at runtime.

Example fix

// before
DispatcherStateOwner.syncStates(token, scene)
// after
if (ProcessSupervisor.isSupervisor) {
    DispatcherStateOwner.syncStates(token, scene)
}
Defensive patterns

Strategy: validation

Validate before calling

if (!ProcessSupervisor.isSupervisor) {
    MatrixLog.w(TAG, "syncStates is supervisor-only, skipping")
    return
}
DispatcherStateOwner.syncStates(token, scene)

Type guard

fun canSyncStates(): Boolean =
    runCatching { ProcessSupervisor.isSupervisor }.getOrDefault(false)

Try / catch

try {
    DispatcherStateOwner.syncStates(token, scene)
} catch (e: IllegalStateException) {
    MatrixLog.w(TAG, "syncStates called outside supervisor", e)
}

Prevention

When it happens

Trigger: Application code (or a subordinate process) calling DispatcherStateOwner.syncStates(token, scene) directly; accidentally linking or invoking the supervisor-only static method from non-supervisor code paths.

Common situations: Trying to force a state sync after seeing stale owner states; copy-pasting supervisor-side code into a subordinate process; custom dispatch logic that bypasses ProcessSupervisor.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/lifecycle/supervisor/DispatcherStateOwner.kt:46

        fun ownersToProcessTokens(context: Context) =
            dispatchOwners.values
                .map { ProcessToken.current(context, it.name, it.attachedSource.active()) }
                .toTypedArray()

        fun dispatchOn(name: String) {
            dispatchOwners[name]?.dispatchOn()
        }

        fun dispatchOff(name: String) {
            dispatchOwners[name]?.dispatchOff()
        }

        /**
         * call from supervisor
         */
        fun syncStates(supervisorToken: ProcessToken, scene: String) {
            if (!ProcessSupervisor.isSupervisor) {
                throw IllegalStateException("call forbidden")
            }
            dispatchOwners.forEach {
                val active = it.value.active()
                MatrixLog.i(ProcessSupervisor.tag, "syncStates: ${it.key} $active")
                ProcessSubordinate.manager.dispatchState(supervisorToken, scene, it.key, active)
            }
        }

        fun attach(application: Application) {
            dispatchOwners.forEach {
                it.value.apply {
                    attachedObserver?.let { o -> attachedSource.removeObserver(o) } // prevent double-observe
                    attachedObserver = object : IStateObserver, ISerialObserver {
                        override fun on() {
                            MatrixLog.d(ProcessSupervisor.tag, "attached ${it.key} turned ON")
                            safeApply("${ProcessSupervisor.tag}.${it.key}") {
                                ProcessSupervisor.supervisorProxy?.onStateChanged(
                                    ProcessToken.current(application, it.key, true)

View on GitHub (pinned to 3b8293bd65)