← All posts

TESTING GUIDES & FEEDBACK

How to Write a Reproducible Android Bug Report: A Practical Template

Checked against the official Android Developers and Android Open Source Project debugging documentation. Confirm current tool options before standardising a process around them.

A vague bug report costs more than it saves. “Upload is broken” says nothing about the screen, the build, the device, or what happened before the failure. The report bounces back for clarification, the tester has moved on, and the fix waits. A report another person can follow is faster to write than the argument that follows a bad one.

This guide gives you a format to copy, two worked examples, and a triage-and-retest workflow. The closed-testing feedback guide covers a whole test cycle; this one narrows in on one report.

Quick answer: make the report repeatable

The short version

  • One issue per report. Combined reports get fixed in part, or not at all.
  • Title the symptom, not your guess at the cause.
  • Record app version, device, and Android version. Those three remove most round trips.
  • Number the steps from a known starting state, then separate expected from actual.
  • State frequency honestly: always, sometimes with counts, or once.
  • No template is a Google Play requirement, and none guarantees production access.

What makes a bug report reproducible

A report is reproducible when a different person, on a comparable setup, can make the same thing happen by following the text. That is the whole test. It does not require a perfect explanation of the cause.

  • A known starting state: a specific account type, screen, and content. If the bug needs a fresh install, say so.
  • Exact steps in order, with the input used — the code entered, the file size, the text typed.
  • An observable symptom. “The progress bar stops at 100% with no error” is observable. “It fails” is an interpretation.
  • The environment: app version, device, Android version, and any condition that plausibly changes behaviour.
  • Frequency, because “sometimes” and “always” are different investigations.
From report to verified fix A tester reports an issue with steps and evidence, a developer triages it, reproduces it on the reported build, fixes it, and the original reporter retests. From report to verified fix Each step removes one kind of guesswork. 1 Tester reports one issue Steps, expected, actual, version, device. A recording only when it adds something. 2 Developer triages Severity from impact, priority from the plan. Ask for missing detail rather than guessing. 3 Reproduce on the reported build Same version, same conditions, same account. If it fails to reproduce, record what you tried. 4 Fix and regression-check Confirm the cause before changing behaviour. Check nearby flows that share the same code. 5 Original reporter retests Same steps, new build, stated outcome. Close it only when the symptom is gone.
Figure 1 — A report becomes a fix only when someone can reproduce it and the original reporter can confirm the change.

Essential information to collect

Field Why it matters Example
App version and build Many reports describe a build already replaced. 2.4.1 (versionCode 214)
Device manufacturer and model Firmware, power management, and hardware vary. Pixel 7a, Galaxy A54
Android version and API level Permission and background rules change by version. Android 15 (API 35)
Preconditions A state-dependent bug looks random without them. Signed in, one item in cart
Expected result The contract the app failed to meet. “The total drops and shows a discount line.”
Actual result The observable failure, including any message. “Total unchanged, no message.”
Frequency Separates a consistent defect from an intermittent one. Always (5 of 5 attempts)
Impact Drives severity and says who is affected. Blocks checkout for every user

A Logcat excerpt or a full device bug report is optional and usually requested by the developer; the next sections explain when each one earns its place.

Add network state, permission results, locale, or font size only when they plausibly changed what the tester saw.

A copy-and-paste Android bug report template

Copy the block into a form, an issue tracker, or a shared document. Adapt the optional section to your app, and delete fields that do not apply rather than guessing at them.

Bug title:
App version / build:
Device manufacturer and model:
Android version (and API level, if shown):
Date and time (with time zone):
Preconditions / starting state:

Steps to reproduce:
1.
2.
3.

Expected result:
Actual result:
Frequency (always / sometimes / once, with counts):
Impact (who is affected and what they cannot do):
Attachments (screenshot / recording / logs):
Reporter and contact:
Additional notes:

Optional - include only what you verified:
Account type or plan:
Network state (Wi-Fi / mobile data / offline):
Permissions granted or denied:
Battery saver or background restrictions:
Locale, font size, display size:
Accessibility services enabled:

Write expected and actual from the user's point of view, not the code's. Anything you did not observe belongs in Additional notes marked “unverified”, so a reader can tell a fact from a theory.

Weak report versus useful report

Both examples are illustrative, written for this guide. They are not customer incidents and they do not describe measured results.

Example 1 — a reproducible functional bug

Weak version: “Discount code is broken. Please fix.”

Useful version:

Bug title: Checkout total does not update after a valid discount code
App version / build: 2.4.1 (versionCode 214)
Device: Google Pixel 7a
Android version: Android 15 (API 35)
Preconditions: Signed in; one item in the cart; no prior discount

Steps to reproduce:
1. Open the cart with one item.
2. Tap "Apply code" and enter SAVE10.
3. Tap "Apply".
4. Wait for the confirmation toast, then view the cart summary.

Expected result: A discount line appears and the total drops by the
advertised amount.
Actual result: The toast confirms the code and the discount line
appears, but the total is unchanged. Removing and re-adding the item
corrects it.

Frequency: Always, 5 of 5 attempts.
Impact: Every discounted order shows the wrong total at checkout.
Attachments: 11-second recording of steps 2 to 4.
Additional notes: Support believes payment charges the discounted
amount. Unverified.

Why it works: the steps start from a state anyone can rebuild, the input is exact, and the results describe the screen rather than a theory.

Example 2 — an intermittent, device-specific issue

Bug title: App closes when switching from Wi-Fi to mobile data
App version / build: 2.4.1 (versionCode 214)
Device: Samsung Galaxy A54 5G (SM-A546B)
Android version: Android 14 (API 34), One UI build
Preconditions: Signed in; an episode playing in the background

Steps to reproduce:
1. Start playback over Wi-Fi.
2. Send the app to the background for 30 seconds.
3. Turn Wi-Fi off so the device switches to mobile data.
4. Return to the app.

Expected result: Playback continues, or a buffering state appears.
Actual result: The app closes with no error message. On reopening,
playback restarts from the beginning.
Frequency: 3 of 9 attempts. Two failures had the screen locked; all
three followed a network handover.
Impact: Listeners lose their position; reach not yet known.
Additional notes: Hypothesis, unverified - a background-thread
callback. No stack trace yet.

Why it works: it gives the exact device build, the attempt count, and the conditions that coincided with each failure — and labels a hypothesis instead of presenting it as a diagnosis.

Symptom, hypothesis, confirmed cause A tester observes a symptom, a hypothesis is proposed but unconfirmed, and only evidence such as a stack trace confirms the cause. Three different things Only the last one belongs in a bug title. Symptom — observed “The app closes after the network switches.” A tester can write this. It is a fact. Hypothesis — unconfirmed “A network callback runs on a background thread.” Label it. It may be wrong, and that is fine. Cause — confirmed by evidence A stack trace, a log line, or a repeatable path. A developer states this, not the reporter.
Figure 2 — Keep the observation, the guess, and the confirmed cause in separate places.

How to write precise reproduction steps

  • Start from a state you can rebuild: “fresh install”, “signed in as a free account”, “cart contains one item”.
  • One action per line. Merged actions hide the step that breaks.
  • Use the visible label: tap “Apply”, not “confirm the change”.
  • Include the exact input: the code entered, the file size, the quantity.
  • Keep interpretation out. “The app freezes” belongs in Actual result, not in step 4.

If a flow is long, check whether the report is really about one screen. A direct-navigation precondition — a deep link, a saved state — often turns ten steps into three.

Severity versus priority

Severity describes how much the defect damages the product. Priority describes the order in which the team will work on it. They can point in opposite directions: a crash on a screen nobody reaches is severe but low priority; a wrong price label is minor but urgent.

Situation Severity Priority Why
Checkout fails for every user High Immediate Blocks revenue and the core task.
Wrong price on the main product page Medium Immediate Affects money and trust without a crash.
Crash in an unreachable internal screen High Low A real defect with no user exposure.
Label wraps at 130% font size Low Medium Cosmetic, but visible to accessibility users.
Session expires silently after 30 minutes Medium High Recoverable, but it interrupts work repeatedly.

Testers usually judge severity best, because they see the impact. Priority needs release context, so a developer or product owner sets it. If your team does not separate the two, use one impact field and describe who is affected.

What to do when a bug is intermittent

“Could not reproduce” is a result, not a failure — as long as the attempt is recorded.

  1. Record attempts as a ratio. “3 of 9 attempts” says more than “sometimes”.
  2. Note what differed on failures: screen locked, low battery, background data, another app in front.
  3. Capture the moment, not the memory. A screenshot taken immediately beats a description written an hour later.
  4. Ask for the smallest extra evidence that exists — a crash timestamp, an error code, a recording. Logs only if the team explains how to collect them.
  5. Do not close it without a retest plan. State what would change the conclusion.

This is where symptom and cause separate most sharply. Android's bug-report documentation notes that the main-thread stack in an ANR trace shows only what that thread was doing at that moment, and may not be the real cause. A report that presents a stack frame as the reason can send a fix the wrong way.

When screenshots, recordings, Logcat, or a full bug report help

These are different tools with different costs. None is mandatory for every report.

Evidence Best for Limitation
Screenshot Layout, wording, wrong values, missing elements. No timing, sequence, or anything off-screen.
Short screen recording Sequences, gestures, the timing of a failure. May capture personal data; rarely proves cause alone.
Logcat excerpt Exceptions, crash traces, SDK errors near a known time. Needs a capture tool; noisy without a time window.
Device bug report ANRs, deadlocks, dumpsys output, device logs. Needs Developer options or adb; large, full of personal data.
Play Console crash and ANR data What real users hit, including stack traces. Aggregated, delayed, and without a reproduction path.

Android Studio's bug-reporting guidance asks for exact steps, the expected and observed outcome, a descriptive title, and — for Studio issues — a diagnostic bundle. That shows proportion: Google asks for a bundle for Studio bugs, not a system dump from every tester of your app.

A full device bug report is a developer task. It needs Developer options, or adb bugreport from a machine with the platform tools. The archive holds dumpsys, dumpstate, and logcat output — right for an ANR or framework failure, overkill for a mislabelled button.

Privacy and security: redact before you share

Diagnostic evidence is user data. A logcat capture, recording, or device bug report can contain email addresses, phone numbers, message contents, account identifiers, and session tokens.

  • Never ask for passwords, OTPs, or access tokens. Issue a dedicated test account if a flow needs credentials.
  • Redact before uploading: names, emails, card digits, and anything not needed to reproduce the issue.
  • Check the whole screenshot, including the notification shade and status bar.
  • Do not post bug-report archives, or links to them, publicly. Share them privately.
  • Keep retention short, and delete attachments when the issue closes.
  • Plan deobfuscation. For a minified build, traces are readable only with the matching mapping file.

A report can be perfectly reproducible and still be unsafe to circulate. Check both.

How developers review, reproduce, prioritise, and retest

  1. Triage on arrival. Confirm it is one issue, check for duplicates, set severity from impact.
  2. Reproduce on the reported build first. A newer build proves the bug existed, not that it still does.
  3. Reply with what you tried, naming the device and build so the tester can narrow the difference.
  4. Confirm the cause before fixing. A plausible cause and a confirmed one need different amounts of rework.
  5. Ship to the same track and ask the original reporter to retest. Confirmation by another tester on another device is weaker evidence.
  6. Record the outcome: version, date, reporter, result.

A practical checklist for a closed-testing team

  • ☐ One reporting channel, agreed before day one, with a link testers can find.
  • ☐ Testers know the current version code and where to find it.
  • ☐ The template sits where testers work: a pinned message, a form, or a shared doc.
  • ☐ Demo accounts exist so nobody shares real credentials.
  • ☐ A named person triages daily and replies, even to say “cannot reproduce”.
  • ☐ Every fix has a retest owner, and attachments have a deletion date.

The 14-day closed-testing checklist covers the rest of the cycle. For crash and ANR data, see Android Vitals explained. For a wider device and OS spread, the compatibility testing guide explains how to build one.

Frequently asked questions

Does Google Play require a specific bug-report format?

No. Play requires closed testing and a summary of the feedback you received; it does not prescribe fields, a template, or a tool.

Do testers need to enable Developer options?

Not by default. Only a full device bug report needs Developer options or adb. Most closed-test reports work with a description, a screenshot, and a version number.

Should testers include a suspected root cause?

Only when it is labelled as a hypothesis. An unlabelled guess in the title or the actual-result field makes a report look more certain than it is.

Can a recording replace logs?

Sometimes, when the symptom is visual and repeatable. A recording cannot show a stack trace, so for crashes and ANRs the log or crash report is better evidence.

Official sources

The report is the unit of quality

A reproducible report is short, literal, and honest about what is unknown. It separates symptom from hypothesis, expected from actual, and severity from priority. Do that consistently and developers fix instead of asking, while testers get an answer instead of a silent backlog.

If you need real testers for a closed test, 12 Testers Community provides that service. It does not replace a clear reporting process, and no testing service can guarantee production access.

Ready for your 12 testers?

Stop reading — start testing. Real people on real devices, 14 days, zero chasing.

WhatsApp