Tencent/matrix · error · RuntimeException
sBuffer == null
Error message
sBuffer == null
What it means
AppMethodBeat.onStart() throws this RuntimeException when the method is called while sBuffer (the internal long[] ring buffer that records method trace timestamps) is still null. sBuffer is allocated during AppMethodBeat.init(); starting the tracer without a buffer would leave the whole method-instrumentation pipeline with nowhere to write, so the library fails fast under statusLock.
Solutions
- Ensure AppMethodBeat.init(config) is called (via TracePlugin.onCreate) before any call to onStart/stopAppMethodBeat
- Start the plugin through Matrix's plugin lifecycle (Matrix.Builder with TracePlugin) instead of calling core APIs directly
- Check that no code path calls stopAppMethodBeat during initialization or re-entry
- Verify status handling: re-init after a previous release to reallocate sBuffer
Example fix
// before TracePlugin trace = new TracePlugin(config); trace.start(); // onStart called before init allocated sBuffer // after Matrix.with().getPlugin(TracePlugin.class); // ensure plugin initialized first AppMethodBeat.init(config); // allocates sBuffer AppMethodBeat.onStart();
Defensive patterns
Strategy: try-catch
Validate before calling
if (AppMethodBeat.isAppStartTimeCalled() || statusCheck()) { /* ensure init called */ AppMethodBeat.init(config); } Type guard
if (!tracePlugin.isInitialized()) { tracePlugin.init(config); } Try / catch
try { AppMethodBeat.onStart(); } catch (RuntimeException e) { MatrixLog.e(TAG, "start before init", e); AppMethodBeat.init(config); AppMethodBeat.onStart(); } Prevention
- Always initialize Matrix/TracePlugin in Application.onCreate on the main thread
- Never call low-level AppMethodBeat start/stop APIs directly; use the plugin lifecycle
- Pair every release with a subsequent re-init
- Add startup-order assertions in debug builds
When it happens
Trigger: Calling stopAppMethodBeat() (which calls onStart) or AppMethodBeat.onStart() directly before init()/onCreate() has allocated sBuffer, or after the buffer was released without re-initializing. The status check (STATUS_EXPIRED_START <= status < STATUS_STARTED) passes because the delayed realReleaseRunnable was cancelled, but the buffer was never allocated.
Common situations: Startup race where TracePlugin is started before AppMethodBeat.init() completes; calling start APIs manually during Application lifecycle before the plugin's onCreate; a prior release/reset leaving status inconsistent with the buffer state.
Related errors
- <getTag()> is not yet init!
- LeakProcessor not found!!!
- origin == this
- must be init in main thread!
- NOT initialized yet
AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08).
Data as JSON: /api/errors/42b420339e98dc79.
Report an issue: GitHub.
Appendix: source
Thrown at matrix/matrix-android/matrix-trace-canary/src/main/java/com/tencent/matrix/trace/core/AppMethodBeat.java:132
}
} catch (Exception e) {
MatrixLog.e(TAG, "" + e.toString());
}
}
};
public static AppMethodBeat getInstance() {
return sInstance;
}
@Override
public void onStart() {
synchronized (statusLock) {
if (status < STATUS_STARTED && status >= STATUS_EXPIRED_START) {
sHandler.removeCallbacks(checkStartExpiredRunnable);
MatrixHandlerThread.getDefaultHandler().removeCallbacks(realReleaseRunnable);
if (sBuffer == null) {
throw new RuntimeException(TAG + " sBuffer == null");
}
MatrixLog.i(TAG, "[onStart] preStatus:%s", status, Utils.getStack());
status = STATUS_STARTED;
} else {
MatrixLog.w(TAG, "[onStart] current status:%s", status);
}
}
}
@Override
public void onStop() {
synchronized (statusLock) {
if (status == STATUS_STARTED) {
MatrixLog.i(TAG, "[onStop] %s", Utils.getStack());
status = STATUS_STOPPED;
} else {
MatrixLog.w(TAG, "[onStop] current status:%s", status);
}View on GitHub (pinned to 3b8293bd65)