---
title: "Events & exceptions — Sluggable for Laravel | Roundly"
description: "SlugChanged, SlugCollisionRetried and SlugsRegenerated events, plus the typed exception hierarchy behind every failure."
url: https://roundly-consulting.com/open-source/docs/sluggable-for-laravel/events
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Sluggable for Laravel](https://roundly-consulting.com/open-source/docs/sluggable-for-laravel.md)

# Events & exceptions

Listen for SlugChanged to refresh sitemaps, caches or search indexes whenever a slug changes:

```php
use RoundlyConsulting\Sluggable\Events\SlugChanged;

Event::listen(SlugChanged::class, function (SlugChanged $event): void {
    // $event->model, ->column, ->locale, ->previous, ->current, ->reason (ChangeReason)
    Cache::forget('sitemap');
});
```

| Event | Payload | When |
| --- | --- | --- |
| `SlugChanged (after commit)` | model, column, locale, previous, current, reason (ChangeReason) | Once per persisted change. |
| `SlugCollisionRetried` | modelClass, column, attempt | Every race retry. |
| `SlugsRegenerated` | modelClass, report | End of a regeneration run / queued chunk. |

- SlugChanged implements ShouldDispatchAfterCommit — never fired for a rolled-back save. It fires once per persisted change, manual ones included, and once per changed locale for maps.
- It is not dispatched inside withoutGeneration() or with sluggable:regenerate --without-events.
- ChangeReason is one of Generated, Regenerated, Manual, CollisionRetry or Restored.

## Exceptions

Every exception extends the abstract SluggableException (a RuntimeException). Messages name the model class, column and locale — never another row’s value.

| Exception | Thrown when |
| --- | --- |
| `InvalidSlugDefinitionException` | An invalid definition or config value — duplicate columns, several route keys, a bad separator, maxLength out of range, a missing contract, a missing scope value and similar. |
| `InvalidLocaleException` | A malformed locale code you supply (malformed keys in stored maps are skipped instead). |
| `SlugGenerationException` | Probing exhausted, an empty source under Fail, retries used up, an index/definition mismatch, invalid custom-slugger output or collision suffix, affixes that leave no room for the slug, an unsafe manual value or a Strict manual value over maxLength. |
| `SlugAlreadyTakenException` | A Strict manual value is already taken. |
| `SlugLockedException` | A manual change to a locked slug; the message is the sluggable::validation.locked translation, in the current locale. |
| `SlugMovedException` | A retired slug was requested — renders the history redirect; implements ShouldntReport. |
| `UnsupportedDriverException` | Index DDL on an unsupported driver (e.g. SQL Server). |

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/sluggable-for-laravel.md)
