Built-In Bug Reporting: How We Built a Universal Error Reporting System
Discover how Reliable Communications built a universal bug reporting system for AvatarMatch, enhancing user experience and error tracking.

Every product eventually faces the same problem: users find bugs but don’t know how to report them. An email to support@? A screenshot in a messenger? A social media post? The harder the path from discovering a problem to fixing it, the more bugs go unreported.
At Reliable Communications s.r.o., we solved this problem systematically — and built an embedded bug reporting platform that works directly inside the application. It’s already running in production at AvatarMatch, but it’s designed as a universal module suitable for any web product.
In this article, we’ll take a detailed look at the architecture, the processing pipeline, and the capabilities of the system.
The Problem: Why External Trackers Don’t Work for End Users
Jira, Linear, GitHub Issues — all great tools for development teams. But try asking an ordinary user to create an issue in GitHub. The result is predictable: 95% of bugs simply won’t get reported.
The classic “email us” approach is equally flawed:
- The user won’t specify the URL where the error occurred
- Won’t include the browser version and OS
- Won’t describe reproduction steps
- Support will spend time on follow-up questions
- The bug will get lost in an email thread
What you need is a tool that’s embedded in the product, collects context automatically, and gives the user a simple, clear form.
System Architecture
Components
The system consists of several layers:
Layer Technology Purpose Floating Action Button (FAB) React + Lucide Icons Entry point — visible on every page Drawer with form React Collects description, expected behavior, steps, attachments Attachment uploader React + Firebase Storage Images (PNG, JPEG, WebP) and videos (MP4, WebM) Auto-context collection Client-side JS URL, browser, OS, device, viewport, locale, timezone, app version REST API Astro SSR endpoints Creation, listing, filtering, moderation Storage Firestore Bug documents, timeline events, counters Bug portal Astro + React View all/my reports, detailed report card AI triage Server pipeline Automatic validation, normalization, classification GitHub integration Admin API Link to issues, status sync Analytics Custom Events Tracking user actions throughout the funnel
The Floating Button — Entry Point
On every page of the application (except authentication pages: /login, /register, /forgot-password, /auth/*, /wizard), a floating button with a bug icon is displayed. The exclusion list is configurable via the PUBLIC_BUG_REPORT_EXCLUDE_PREFIXES environment variable.
Clicking it opens a Drawer — a side panel with the report form. If the user isn’t authenticated, they’ll see a login prompt. If the form has been filled in and the user tries to close the drawer, the system shows a discard confirmation.
The Report Form
The form contains the following fields:
- Title (optional, up to 120 characters) — a brief description of the problem
- What happened (required, 10–5,000 characters) — a detailed bug description
- Expected behavior (required, 5–3,000 characters) — what should have happened
- Steps to reproduce (optional, up to 3,000 characters) — step-by-step instructions
- Visibility — private or public report
- Attachments — up to 5 files: images up to 10 MB, videos up to 100 MB (up to 90 seconds long)
Validation happens both client-side (instant hints) and server-side (re-checking all constraints).
Automatic Context Collection
One of the system’s key features is automatic technical context collection at the moment of report submission. The user doesn’t need to know anything about their browser — the system detects everything automatically:
- Page URL and route name
- Page title
- Browser and version (Chrome, Safari, Firefox, Edge)
- Operating system (Windows, macOS, Android, iOS, Linux)
- Device type (desktop, mobile, tablet)
- Viewport and screen resolution
- Locale and timezone
- App version
- Session ID (generated client-side, stored in sessionStorage)
This context is sent to the server along with the form and saved in the bug document. When viewing the report in the portal, a developer sees the complete picture of the environment where the error occurred.
Bug Lifecycle: From PendingCheck to Resolved
Every bug report goes through a formalized status pipeline:
PendingCheck → Normalizing → Triaging → RepoChecking → Confirmed → InProcess → Open → Resolved
↘ Closed
↗ Cancelled
↗ Duplicate
↗ Rejected
Status Breakdown
Status Meaning PendingCheck Report just created, awaiting initial review Normalizing AI normalizes text: fixes formatting, extracts structure Triaging AI determines priority, category, and potential impact level RepoChecking Checking for duplicates among existing reports Confirmed Bug confirmed, awaiting assignment InProcess Fix is being worked on Open Bug is open and registered in the tracker Resolved Fix has been completed Closed Report closed (manually or automatically) Cancelled Cancelled by author or moderator Duplicate Marked as a duplicate of an existing report Rejected Declined (not a bug, unreproducible, out of scope)
Terminal statuses — Cancelled, Closed, Duplicate, Rejected, Resolved — block further content editing and cancellation of public reports by the author.
AI Triage: Automatic Report Processing
After creation, a report passes through a server-side AI pipeline that performs several steps:
- Normalization — structuring text, fixing obvious typos, extracting key data
- Triage — automatic determination of severity (impact level), category, and priority
- Duplicate detection — comparison with existing open reports
- Validation — filtering out spam, invalid, or empty reports
Each step logs token usage (prompt, completion, total), enabling monitoring of AI processing costs. Token usage data is visible to administrators in the report detail view — with per-step breakdowns.