Exception grouping

Errors are automatically grouped based on their stack trace and other identifying characteristics. This helps you see a consolidated view of similar issues, reducing noise and making it easier to prioritize and resolve problems.

When you view the Errors, you'll see a list of these grouped errors. Each group displays the total number of occurrences, affected users, and a trend chart so you can quickly assess its impact.

Click on any error group to see its detailed view:

  • Exceptions: Individual occurrences with timestamps and user details.
  • Users: A list of all affected users.
  • Tags: Metadata and context extracted from the errors.
  • Fix with AI: AI-generated explanations and debugging assistance.

See error details

Manage the errors

  • Resolve: Mark errors as fixed once you've addressed them.
  • Ignore: Silence errors you don't need to see, with options to ignore a specific number of future occurrences.
  • Unresolve: Reopen an error if it reoccurs or if you change your mind.
  • Bulk actions: Apply these actions to multiple error groups at once for efficient management.

Bulk error actions

How does it work

By default, Better Stack generates a ._pattern field, which is a hash generated from the characteristics of an error using Vector Remap Language (VRL). For complete details on VRL syntax and capabilities, please refer to the official VRL documentation.

If the error payload contains a fingerprint array, it's used as the primary grouping key. This gives you direct control from your application code.

Otherwise, it combines the call_stack_hash, type, and message of the error. This is the most common grouping logic.

Because it includes the message, errors like these will be treated as separate groups by default:

  • User '123' not found.
  • User '456' not found.

See it in ErrorsApplications → Your application → Transformation.

Default VRL transformation
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
  to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
  if exists(.summary.type) && .summary.type != null {
    to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type) + ":" + to_string!(.summary.message)
  } else {
    to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.message)
  }
} else if exists(.summary.type) && .summary.type != null {
  to_string!(.summary.type) + ": " + to_string!(.summary.message)
} else {
  to_string!(.summary.message)
}

._pattern = sha2(_hash_input)

Customize exception grouping

In ErrorsApplications → Your application → Transformation, you can modify the VRL script to change what information is included in the _hash_input.

By altering this input, you can omit the message from the calculated _pattern, remove numerical IDs from it, add a custom tag such as service_name to differentiate between your services, or tweak it in any other way:

Omit message Omit ids in message Add a custom tag
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
  to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
  if exists(.summary.type) && .summary.type != null {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type)
} else {
to_string!(.summary.call_stack_hash)
} } else if exists(.summary.type) && .summary.type != null { # Keep message if no call stack is provided to_string!(.summary.type) + ": " + to_string!(.summary.message) } else { to_string!(.summary.message) } ._pattern = sha2(_hash_input)
_reduced_message = replace(to_string!(.summary.message), r'\d+', "<id>")
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 { to_string(.fingerprint) ?? "" } else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null { if exists(.summary.type) && .summary.type != null { to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type) + ":" + _reduced_message } else { to_string!(.summary.call_stack_hash) + ":" + _reduced_message } } else if exists(.summary.type) && .summary.type != null { to_string!(.summary.type) + ": " + _reduced_message } else { _reduced_message } ._pattern = sha2(_hash_input)
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
  to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
  if exists(.summary.type) && .summary.type != null {
    to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type) + ":" + to_string!(.summary.message)
  } else {
    to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.message)
  }
} else if exists(.summary.type) && .summary.type != null {
  to_string!(.summary.type) + ": " + to_string!(.summary.message)
} else {
  to_string!(.summary.message)
}

if exists(.tags.service_type) && .tags.service_type != null {
_hash_input = _hash_input + ": " + to_string!(.tags.service_type)
}
._pattern = sha2(_hash_input)