Skip to content

fix(bugsnag): group server errors by class and reason - #1930

Merged
bmc08gt merged 1 commit into
code/cashfrom
fix/bugsnag-server-error-grouping
Nov 1, 2026
Merged

bmc08gt merged 1 commit into
code/cashfrom
fix/bugsnag-server-error-grouping

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Bugsnag groups handled errors on the top stack frame. For a CodeServerError that frame is the line that built it, e.g. SubmitIntentError$Companion.typed, so when that line moves the next release opens a new group for the same server result. That's how SubmitIntentError$StaleState "gift card balance has already been claimed" ended up in two groups keyed on lines 213 and 215, and SendMessageError$Denied in four, one per release. #1921 fixed this for ValidationException only.

This implements the Android CodeServerError part of Decision 4 in docs/specs/bugsnag-cleanup.md.

BugsnagErrorReporter.groupingHashFor now returns, in order:

  • ValidationException (error or cause): its existing field:rule hash, unchanged.
  • Any other CodeServerError, with the reported error checked before its cause: <class binary name>:<normalized message>.
  • Anything else: null, so crashes keep Bugsnag's stack grouping.

The message is part of the hash because one class can carry several reasons. StaleState has six ("race detected", "gift card balance has already been claimed", "intent already exists", ...), and only some are notifiable, so a class-only hash would put a WARNING inside the gift card INFO group. None of the error classes expose a structured reason code: SubmitIntentError, SwapError and the rest keep the proto's reason strings in private lists and join them into message. So every class hashes on the normalized message.

normalizeMessage removes [N] indexes and replaces base58 runs of 32+ characters with <address> and numbers (including decimals) with <n>. That keeps messages like SwapError$Denied "fee amount must be 184467440737095440 quarks" and SubmitIntentError$InvalidIntent "actions[0]:

has insufficient..." in one group whatever the amount or account. A base58 run must contain a letter, so a long amount still becomes <n> and the amount's length can't change the hash.

The class name is stable across builds because proguard-rules.pro keeps the names of public Throwable subclasses (-keepnames public class * extends java.lang.Throwable). A scan of the compiled classes found 722 CodeServerError subclasses, all with ACC_PUBLIC set, so the rule covers each of them.

BugsnagErrorReporterTest adds cases for: the same error built at two call sites sharing a hash, the six StaleState reasons getting six hashes, messages that differ only in an amount or address sharing a hash, the same message on two classes staying apart, the reported error winning over a server-error cause, and the hash dropping the CodeServerError: prefix. The ValidationException hash and the null case for other throwables are pinned too.

Bugsnag groups handled errors on the top frame, and for a CodeServerError
that is the line that built it, such as SubmitIntentError.typed. When the
line moves, the next release opens a new group for the same server result.

groupingHashFor now hashes every CodeServerError, as the error or its
cause, on its class name plus its message with list indexes, base58
addresses and numbers replaced by placeholders. ValidationException keeps
its field:rule hash, and other throwables still return null.
@bmc08gt bmc08gt self-assigned this Nov 1, 2026
@github-actions github-actions Bot added the type: fix Bug fix label Nov 1, 2026
@bmc08gt
bmc08gt merged commit f93369d into code/cash Nov 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant