Tencent/matrix · error · AssertionError

must be init in main thread!

Error message

must be init in main thread!

What it means

UIThreadMonitor.init(config) throws an AssertionError when invoked from any thread other than the main (UI) thread. The monitor installs a Looper printer and observes frame callbacks on the main Looper, so initialization off the main thread is unsupported and would produce broken instrumentation.

Solutions

  1. Call init on the main thread, e.g. in Activity/Application.onCreate or via Handler(Looper.getMainLooper()).post(...)
  2. Move TracePlugin initialization into the main app entry point
  3. If init is triggered asynchronously, route it through the main Looper before calling

Example fix

// before
new Thread(() -> UIThreadMonitor.get().init(config)).start();
// after
new Handler(Looper.getMainLooper()).post(() ->
    UIThreadMonitor.get().init(config));
Defensive patterns

Strategy: validation

Validate before calling

if (Looper.myLooper() != Looper.getMainLooper()) throw new IllegalStateException("call init on main thread");

Type guard

boolean isMainThread() { return Looper.myLooper() == Looper.getMainLooper(); }

Try / catch

Runnable init = () -> UIThreadMonitor.get().init(config); if (isMainThread()) init.run(); else new Handler(Looper.getMainLooper()).post(init);

Prevention

When it happens

Trigger: Calling UIThreadMonitor.get().init(config) from a worker thread, background executor, or a non-main handler thread instead of the application main thread (e.g. inside Application.onCreate is fine, but inside an AsyncTask.doInBackground is not).

Common situations: Initializing Matrix/TracePlugin from a background thread at app startup; test harnesses initializing the monitor on a test thread; libraries wrapping Matrix init in their own thread pools.

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/6c76c0078931786d. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/UIThreadMonitor.java:111

    private long[] queueCost = new long[CALLBACK_LAST + 1];
    private static final int DO_QUEUE_DEFAULT = 0;
    private static final int DO_QUEUE_BEGIN = 1;
    private static final int DO_QUEUE_END = 2;
    private boolean isInit = false;

    public static UIThreadMonitor getMonitor() {
        return sInstance;
    }

    public boolean isInit() {
        return isInit;
    }

    public void init(TraceConfig config) {
        this.config = config;

        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
            throw new AssertionError("must be init in main thread!");
        }

        boolean historyMsgRecorder = config.historyMsgRecorder;
        boolean denseMsgTracer = config.denseMsgTracer;

        LooperMonitor.register(new LooperMonitor.LooperDispatchListener(historyMsgRecorder, denseMsgTracer) {
            @Override
            public boolean isValid() {
                return isAlive;
            }

            @Override
            public void dispatchStart() {
                super.dispatchStart();
                UIThreadMonitor.this.dispatchBegin();
            }

            @Override

View on GitHub (pinned to 3b8293bd65)