Skip to content

Android 17 method traces rejected as profiling_invalid_sampled_profile: new ART header keys unsupported by android_trace_log #6517

Open

Description

@antonis

Summary

Android method-trace profiles recorded on Android 17 (API 37) are rejected at ingestion with outcome invalid / reason profiling_invalid_sampled_profile. The same app on Android 16 (API 36) is accepted.

Android 17's ART adds five new lines to the *version header section of method traces. Relay parses sampled_profile with android_trace_log 0.3.0, which fails on any header key it doesn't know. The new lines aren't publicly documented; they're defined only in ART's source.

Android 17, same app Profile
❌ As recorded e32415d4d98a43fbb8db7866cf56d51c → profiling_invalid_sampled_profile (not found)
✅ Unknown header lines stripped on the client 03d14df40eec40538c7b67de18d8fcb6 → accepted

(org sentry-sdks, project sentry-react-native / 5428561, 2026-10-15.) The transactions themselves are accepted; only the profile is dropped. On 2026-10-15 alone, 33 Android 17 profiles from this project were rejected this way.

Where it fails

  1. relay-profiling/src/android/legacy.rs#L138-L140 (transaction profiles) and relay-profiling/src/android/chunk.rs#L100-L102 (Android trace chunks):
    match android_trace_log::parse(&profile_bytes) {
        Ok(profile) => profile,
        Err(_) => return Err(ProfileError::InvalidSampledProfile),
    }
  2. android_trace_log 0.3.0 (Cargo.toml#L87). In its src/lib.rs, the *version header loop only accepts either a new *section or one of ten known keys (data-file-overflow, clock, elapsed-time-usec, num-method-calls, clock-call-overhead-nsec, vm, pid, alloc-count, alloc-size, gc-count). Any other line errors out:
    let (inp, line) = alt((
        map(parse_section, Line::NewSection),
        map(parse_header_line, Line::Info),
    ))(input)?;
  3. Android 17 writes these lines after pid= in every method trace, and the first one aborts the parse:
     *version
     3
     data-file-overflow=false
     clock=dual
     elapsed-time-usec=9407646
     num-method-calls=4341
     clock-call-overhead-nsec=273
     vm=art
     pid=4471
    +is_streaming=false
    +is_precise_trace=true
    +end_time=1642106300035
    +end_tsc=7762889077040
    +compilation-filter=run-from-apk
     *threads

Where the new lines come from

They're written unconditionally by TraceWriter::CreateSummary in ART's runtime/trace.cc on android17-release. The same function on android16-release stops at pid=.

os << "is_streaming=" << (trace_output_mode_ == TraceOutputMode::kStreaming ? "true" : "false") << "\n";
os << "is_precise_trace=" << (flags & Trace::TraceFlag::kTraceLowOverhead ? "false" : "true") << "\n";
os << "end_time=" << end_time << "\n";        // NanoTime()
os << "end_tsc=" << end_time_tsc << "\n";     // TimestampCounter
os << "compilation-filter=" << compiler_filter << "\n";  // primary APK optimization status

None of this is documented outside the source. The Android 17 release notes and behavior-change pages don't mention it, and the trace header format has never been formally specified.

The same function also has two older conditional variations (already in Android 16) that android_trace_log doesn't handle either:

  • elapsed-time-nsec instead of elapsed-time-usec for the V2 trace format;
  • num-method-calls omitted in streaming mode.

Debug.startMethodTracingSampling, which the SDKs use, produces the V1 non-streaming variant, so today only the Android 17 keys are hit. Still, it argues for tolerating unknown keys in general rather than special-casing these five.

The header is the only incompatibility

  • A port of android_trace_log's grammar parses 15/15 Android 17 traces completely once unknown header keys are skipped: binary v3, event_size=14, clock=dual, 44–74 threads, 115–1230 methods, 657–12,791 events, all consumed, 0 trailing bytes. Threads, methods, the SLOW binary header and the event records are unchanged.
  • Verified end to end (table above): stripping only these five lines on the client before upload makes Android 17 profiles ingest. Reference PR, not to be merged: [DO NOT MERGE] Strip Android 17 trace header keys unknown to Relay sentry-react-native#6892.

Impact

  • Every legacy Debug.startMethodTracingSampling transaction profile from an Android 17 device. That covers the React Native SDK (default platformProfilers: true) and, by the same mechanism, sentry-android's transaction profiler.
  • Probably also Android trace profile chunks from the legacy profiler, since chunk.rs uses the same parser. sentry-android's continuous profiling on API 35+ normally uses Perfetto, which limits this part.
  • Already-shipped SDK versions are affected, so a Relay-side fix covers everyone without a client update.
  • It's easy to miss: in Stats this outcome shows as "Invalid · Internal" (see the note at the end).

Suggested fix

Skip header key=value lines whose key is unknown, before or inside android_trace_log::parse, at both call sites (legacy.rs, chunk.rs).

  • Unknown keys only: malformed known keys (e.g. clock=foo) keep failing exactly as today.
  • No regressions: traces without unknown keys stay byte-identical, so current parsing is unaffected.
  • Offsets are safe: data_offset is read relative to the SLOW magic, not the file start, so shortening the text header doesn't move the binary section.
  • Where to fix: upstream chayleaf/android_trace_log was last pushed 2023-09-03, so this likely needs a Relay-side pre-filter or a fork/vendored copy rather than an upstream bump.
  • Test fixture: we can provide Android 17 trace fixtures (15 captured).

Note: Stats shows this as "Internal"

static/app/views/organizationStats/getReasonGroupName.ts falls back to internal for every invalid reason it doesn't group, which covers all profiling_* reasons. The real reason was only visible through stats_v2?groupBy=reason. That probably deserves a small follow-up in getsentry/sentry.

Source references

Relay (pinned to 75ace9c9)

android_trace_log 0.3.0 (crate, upstream repo, last push 2023-09-03)

  • src/lib.rs parse_header_line: the ten accepted header keys
  • src/lib.rs parse_android_trace, *version loop: alt((parse_section, parse_header_line))?, which errors on any other line
  • src/lib.rs parse_num: hex (0x…) or decimal; SLOW header with data_offset relative to the magic

ART (AOSP)

Android docs (no mention of the header change; the format isn't specified)

Sentry

Activity

  1. linear-code commented on Oct 15, 2026

    @linear-code
  2. self-assigned this
    on Oct 16, 2026
  3. antonis commented on Oct 16, 2026

    @antonis
    Author

    Heads up that I've opened a PR for this 🙇 #6523

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions