# 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](https://errors.betterstack.com/team/0/errors/ ";_blank"), 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](https://betterstack.com/docs/errors/using-the-product/fix-with-ai/) and debugging assistance.

![See error details](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/ab474b87-23be-4fb4-69eb-de5c33095700/lg1x =3912x2802)

## 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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/def87422-ae28-4e4e-76a0-1a97177c1a00/lg1x =4160x1576)

## 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](https://vector.dev/docs/reference/vrl/).

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 **Errors** → [Applications](https://errors.betterstack.com/team/t0/applications/ ";_blank") → Your application → **Transformation**.

```vrl
[label Default VRL transformation]
grouping = 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(grouping)
```

## Customize exception grouping

In **Errors** → [Applications](https://errors.betterstack.com/team/t0/applications/ ";_blank") → Your application → **Transformation**, you can modify the VRL script to change what information is included in the `grouping`.

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:

[code-tabs]
```vrl
[label Omit message]
grouping = 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 {
[highlight]
    to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type)
[/highlight]
  } else {
[highlight]
    to_string!(.summary.call_stack_hash)
[/highlight]
  }
} 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(grouping)
```
```vrl
[label Omit ids in message]
[highlight]
_reduced_message = replace(to_string!(.summary.message), r'\d+', "<id>")
[/highlight]

grouping = 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(grouping)
```
```vrl
[label Add a custom tag]
grouping = 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)
}

[highlight]
if exists(.tags.service_type) && .tags.service_type != null {
  grouping = grouping + ": " + to_string!(.tags.service_type)
}
[/highlight]

._pattern = sha2(grouping)
```
[/code-tabs]

