Writing a bug report maintainers can act on

Every open issue tracker has a few reports that sit untouched for weeks. Often nobody is ignoring them on purpose. The maintainer opened the issue, read “it crashes when I run it”, could not tell which version, which system or which input, and moved on to one they could actually work with. A bug report is a request for someone else’s time, and the reports that get fixed are the ones that spend that time well.
This post is about writing that kind of report: one a maintainer can read once, reproduce on their own machine and start fixing. The running example is an imaginary command-line tool called tallycsv that counts rows in a CSV file. The tool is made up; the structure works for any project.
A title that names the symptom and the trigger
The title is what a maintainer sees in a list of fifty open issues, and what the next person with the same problem searches for. It should say what goes wrong and under what condition, in a single line. Compare:
- “tallycsv broken”
- “Crash on large files”
- “
countraises ValueError when a quoted field contains a comma”
The first says nothing. The second guesses at a cause (file size) that may be wrong, which can send everyone down the wrong path. The third names the command, the visible symptom and the input that triggers it. A maintainer reading it may already suspect the part of the code involved, and anyone searching for that error will find it.
Keep your theories out of the title. If you think you know the cause, say so in the body, clearly marked as a guess.
Environment and versions, copied from the terminal
“Latest version on Mac” is not a version. Bugs are often specific to one release, one operating system or one runtime, and the fastest way to rule those in or out is exact output from commands, pasted as text. Run whichever of these apply to the project:
uname -a
git --version
node --version
python3 --version
python3 -m pip --version
python3 -m pip show tallycsvuname -a prints the kernel name, release and machine architecture, which is enough to tell an Apple Silicon Mac from an Intel one or an ARM server from an x86 laptop. On macOS, sw_vers adds the macOS version; on most Linux systems, cat /etc/os-release names the distribution. python3 -m pip --version shows which interpreter pip belongs to, and pip show prints the installed version of the package itself.
Many projects ask for this in an issue template. Fill it in even when it feels obvious. And if the tool has its own version flag, include that output too, because the version on your path is not always the one you think you installed.
The smallest reproduction you can make
This is the part that decides whether the bug gets fixed. A maintainer who can trigger the problem in two minutes will usually look at it. One who has to clone your application, set up a database and guess at the data will usually not.
Start from your real failure and remove things until it stops failing, then put back the last thing you removed. Swap your production data for a few made-up lines. Start in a clean environment, so nothing installed for another project gets in the way:
python3 -m venv /tmp/tallycsv-repro
. /tmp/tallycsv-repro/bin/activate
python -m pip install "tallycsv==2.4.0"
printf 'name,age\n"Smith, J",41\n' > sample.csv
tallycsv count sample.csvFive lines, a pinned version and a two-line input file built by the commands themselves. Anyone can paste that into a terminal and see the same thing you saw. The same rules that make a tutorial reproducible apply here, just on a smaller scale; our post on writing a tutorial someone else can reproduce goes through them in more detail.
If the bug only shows up with a large or private file, say so, describe what is special about it (encoding, line endings, a field longer than some size) and try to generate a synthetic file with the same property. Never attach real customer data, credentials or private keys, even to a private tracker.
Expected, actual and the complete output
State in one sentence each what you expected and what happened. It sounds redundant, but it is not: sometimes the behaviour you call a bug is the documented behaviour, and the maintainer can only tell you that if they know what you expected.
Expected: tallycsv count sample.csv prints 1, because the file has one data row and the comma is inside quotes.
Actual: the command exits with this traceback:
Traceback (most recent call last):
File "/tmp/tallycsv-repro/bin/tallycsv", line 8, in <module>
sys.exit(main())
File "/tmp/tallycsv-repro/lib/python3.10/site-packages/tallycsv/cli.py", line 41, in main
rows = read_rows(args.path)
File "/tmp/tallycsv-repro/lib/python3.10/site-packages/tallycsv/reader.py", line 17, in read_rows
raise ValueError(f"expected {width} fields, saw {len(parts)} on line {n}")
ValueError: expected 2 fields, saw 3 on line 2Paste the whole thing, as text, in a code block. The last line tells the maintainer what went wrong; the lines above it tell them where, and the file paths quietly confirm the Python version and the virtual environment. Cutting the output down to the last line removes exactly the part they need. Screenshots are worse still: nobody can search them or copy a path out of them.
If the tool has a verbose or debug flag, run it again with that flag and include the output as well, in a separate block. Mark anything you edited, such as a home directory replaced with /home/user, so nobody wonders whether the paths are real. Our post on explaining an error message covers reading output like this from the other side, as the person writing the fix.
What you already tried, and what it showed
A short list of what you tested saves the maintainer from asking you to try it. Include the results, not just the attempts, and include the ones that changed nothing:
- The same file with the quotes removed works and prints
1. - Version 2.3.0 does not have the problem:
python -m pip install "tallycsv==2.3.0"in the same environment prints1. - Python 3.10 and 3.12 behave the same.
The second item is especially valuable. A known good version and a known bad version turn the bug into a search problem. If you are comfortable building the project from source, git bisect can find the commit that introduced it:
git bisect start
git bisect bad v2.4.0
git bisect good v2.3.0Git then checks out a commit halfway between the two. You test it, run git bisect good or git bisect bad, and repeat until Git names the first bad commit. Run git bisect reset afterwards to return to where you started. Putting that commit in the report often turns a vague issue into a fix of a few lines. It is optional, though; a clean reproduction with two version numbers is already a good report.
Before you submit
Search the tracker for the error text first, including closed issues. If someone has already reported it, add your environment and reproduction to that issue instead of opening a new one. Read the project’s contributing notes, since some projects want security problems reported privately rather than in a public tracker.
Then read your report as if you knew nothing about your setup. Could you paste the commands and see the failure? Is every version written out? Is the full output there? If yes, submit it, stay subscribed and answer follow-up questions promptly. If you later find a workaround or the cause, add it as a comment, so the next person who lands on the issue from a search gets the whole story.




Comments 0
Moderated before publication. Sorted oldest first.
No comments yet. Spotted a typo, a better flag, or a step that failed for you? Say so below.