Integration
sentry-android
Build System
Gradle
AGP Version
N/A (observed server-side from ingested events)
Proguard
Enabled
Other Error Monitoring Solution
No
Version
8.29.0 (logic unchanged on current main)
Steps to Reproduce
-
Android app using the default ANRv2 path (AnrV2Integration, API 30+ / ApplicationExitInfo).
-
An ANR occurs on a device/ROM where ApplicationExitInfo#getTraceInputStream() returns a native-format thread dump. In that format, thread headers carry the kernel comm name (max 15 chars), so the main thread is named after the process (e.g. "com.example.app"), not "main". Every thread is parsed via BEGIN_UNMANAGED_NATIVE_THREAD_RE (no state, all frames platform:native).
Example header the parser receives (synthetic):
"com.example.app" sysTid=1039
native: #00 pc 0x... /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait)
...
native: #NN pc 0x... /system/bin/app_process64 (main)
-
The SDK parses the dump and builds the ANR event.
Expected Result
The ApplicationNotResponding exception is backfilled with the main thread's stack trace (the documented ANRv2 behavior), regardless of what the OS names the main thread. The main-thread stack is clearly identifiable, it roots at __libc_init → main (app_process64) → AndroidRuntime::start.
Actual Result
The main thread is never identified, so the ANR is reported with no stack trace on the exception, even though the real main-thread stack is present in event.threads.
Root cause: main-thread detection is a strict name match:
Secondary symptom (same code path): for these native dumps, a thread whose section has no parseable frames still gets a stacktrace with frames: [] — parseStacktrace always attaches a stacktrace and there is no isEmpty guard (contrast SentryThreadFactory / SentryExceptionFactory, which skip empty stacks). Ingestion then rejects it, since stacktrace.frames must be non-empty, producing an invalid_data / "expected a non-empty value" processing error on the event and threads.values.N.stacktrace.frames being nulled.
Suggested fix directions
- Detect the main thread robustly for
AppExitInfo dumps instead of an exact "main" match — e.g. the thread whose stack roots at app_process / AndroidRuntime::start, or the thread whose tid equals the process id, or fall back to the process-named thread.
- Do not attach a stacktrace with empty
frames to a SentryThread (mirror the existing !frames.isEmpty() guards), so ingestion does not reject the event with a processing error.
Integration
sentry-android
Build System
Gradle
AGP Version
N/A (observed server-side from ingested events)
Proguard
Enabled
Other Error Monitoring Solution
No
Version
8.29.0 (logic unchanged on current main)
Steps to Reproduce
Android app using the default ANRv2 path (
AnrV2Integration, API 30+ /ApplicationExitInfo).An ANR occurs on a device/ROM where
ApplicationExitInfo#getTraceInputStream()returns a native-format thread dump. In that format, thread headers carry the kernelcommname (max 15 chars), so the main thread is named after the process (e.g."com.example.app"), not"main". Every thread is parsed viaBEGIN_UNMANAGED_NATIVE_THREAD_RE(nostate, all framesplatform:native).Example header the parser receives (synthetic):
The SDK parses the dump and builds the ANR event.
Expected Result
The
ApplicationNotRespondingexception is backfilled with the main thread's stack trace (the documented ANRv2 behavior), regardless of what the OS names the main thread. The main-thread stack is clearly identifiable, it roots at__libc_init → main (app_process64) → AndroidRuntime::start.Actual Result
The main thread is never identified, so the ANR is reported with no stack trace on the exception, even though the real main-thread stack is present in
event.threads.Root cause: main-thread detection is a strict name match:
ThreadDumpParsermarksmainonly ifthreadName.equals("main"):https://github.com/getsentry/sentry-java/blob/main/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java#L210-L217
ApplicationExitInfoEventProcessor.findMainThreadalso matches onlyname.equals("main"):https://github.com/getsentry/sentry-java/blob/main/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoEventProcessor.java#L764-L775
findMainThreadreturnsnull, so the exception is built from a dummy thread with an empty stacktrace:https://github.com/getsentry/sentry-java/blob/main/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoEventProcessor.java#L801-L808
Secondary symptom (same code path): for these native dumps, a thread whose section has no parseable frames still gets a stacktrace with
frames: []—parseStacktracealways attaches a stacktrace and there is noisEmptyguard (contrastSentryThreadFactory/SentryExceptionFactory, which skip empty stacks). Ingestion then rejects it, sincestacktrace.framesmust be non-empty, producing aninvalid_data/ "expected a non-empty value" processing error on the event andthreads.values.N.stacktrace.framesbeing nulled.Suggested fix directions
AppExitInfodumps instead of an exact"main"match — e.g. the thread whose stack roots atapp_process/AndroidRuntime::start, or the thread whose tid equals the process id, or fall back to the process-named thread.framesto aSentryThread(mirror the existing!frames.isEmpty()guards), so ingestion does not reject the event with a processing error.