{"record":{"id":"10f3dcbb93a31339","repo":"Tencent/matrix","slug":"origin-this","errorCode":null,"errorMessage":" origin == this","messagePattern":" origin == this","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/LooperMonitor.java","lineNumber":326,"sourceCode":"        }\n    }\n\n\n    class LooperPrinter implements Printer {\n        public Printer origin;\n        boolean isHasChecked = false;\n        boolean isValid = false;\n\n        LooperPrinter(Printer printer) {\n            this.origin = printer;\n        }\n\n        @Override\n        public void println(String x) {\n            if (null != origin) {\n                origin.println(x);\n                if (origin == this) {\n                    throw new RuntimeException(TAG + \" origin == this\");\n                }\n            }\n\n            if (!isHasChecked) {\n                isValid = x.charAt(0) == '>' || x.charAt(0) == '<';\n                isHasChecked = true;\n                if (!isValid) {\n                    MatrixLog.e(TAG, \"[println] Printer is inValid! x:%s\", x);\n                }\n            }\n\n            if (isValid) {\n                dispatch(x.charAt(0) == '>', x);\n            }\n\n        }\n    }\n","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/LooperMonitor.java#L308-L344","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nLooperMonitor.register(listener);\nLooperMonitor.register(listener); // second wrap: origin == this\n// after\nLooperMonitor.unRegister();\nLooperMonitor.register(listener);","handlingStrategy":"validation","validationCode":"if (LooperMonitor.isRegistered()) { LooperMonitor.unRegister(); } // before re-registering","typeGuard":"assert !LooperMonitor.isValid || !isSelfRegistered();","tryCatchPattern":"try { LooperMonitor.register(listener); } catch (RuntimeException e) { Log.w(TAG, \"printer already installed\", e); LooperMonitor.unRegister(); LooperMonitor.register(listener); }","preventionTips":["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"],"tags":["android","matrix","looper","printer","double-registration"],"backgroundTag":"invalid-state-transition","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}