Skip to content

ANRv2 (AppExitInfo): main thread not detected when the OS thread dump names it after the process → ANR reported with no stack trace #5731

Description

@Kobby-Bawuah

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

  1. Android app using the default ANRv2 path (AnrV2Integration, API 30+ / ApplicationExitInfo).

  2. 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)
    
  3. 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

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions