Tencent/matrix · error · RuntimeException
origin == this
Error message
origin == this
What it means
LooperMonitor's Printer (LooperObserver) dispatches messages to the previously-registered origin printer and then throws if origin == this, i.e. the monitor itself has been chained into the Looper's printer list twice. That would cause infinite/degenerate dispatch loops, so the library detects the self-referential registration and throws.
Solutions
- Ensure LooperMonitor.unRegister/stop is called before registering again (match init/release lifecycle)
- Do not call register() twice; guard with an isValid/installed flag
- Restart the plugin only after its full release cycle completes
- Check for duplicated plugin start from multiple entry points
Example fix
// before LooperMonitor.register(listener); LooperMonitor.register(listener); // second wrap: origin == this // after LooperMonitor.unRegister(); LooperMonitor.register(listener);
Defensive patterns
Strategy: validation
Validate before calling
if (LooperMonitor.isRegistered()) { LooperMonitor.unRegister(); } // before re-registering Type guard
assert !LooperMonitor.isValid || !isSelfRegistered();
Try / catch
try { LooperMonitor.register(listener); } catch (RuntimeException e) { Log.w(TAG, "printer already installed", e); LooperMonitor.unRegister(); LooperMonitor.register(listener); } Prevention
- Match every register() with unRegister()
- Never start the trace plugin twice without releasing
- Check plugin state (isValid) before re-entry
- Avoid concurrent registration from multiple threads
When it happens
Trigger: Calling LooperMonitor.register()/start() twice without an intervening unregister/stop, so setMessageLogging wrapped the monitor with itself as origin; concurrent registration from multiple threads during the check window.
Common situations: Trace plugin restarted (onStart/onStop cycles) without LooperMonitor releasing the old printer; a crash or missed unregister leaving the monitor installed, then a second install; re-init of UIThreadMonitor while the Looper still holds the old printer.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- <getTag()> is not yet init!
- sBuffer == null
- must be init in main thread!
- Matrix init, Matrix should not be null.
- you must init Matrix sdk first
AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08).
Data as JSON: /api/errors/10f3dcbb93a31339.
Report an issue: GitHub.
Appendix: source
Thrown at matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/LooperMonitor.java:326
}
}
class LooperPrinter implements Printer {
public Printer origin;
boolean isHasChecked = false;
boolean isValid = false;
LooperPrinter(Printer printer) {
this.origin = printer;
}
@Override
public void println(String x) {
if (null != origin) {
origin.println(x);
if (origin == this) {
throw new RuntimeException(TAG + " origin == this");
}
}
if (!isHasChecked) {
isValid = x.charAt(0) == '>' || x.charAt(0) == '<';
isHasChecked = true;
if (!isValid) {
MatrixLog.e(TAG, "[println] Printer is inValid! x:%s", x);
}
}
if (isValid) {
dispatch(x.charAt(0) == '>', x);
}
}
}
View on GitHub (pinned to 3b8293bd65)