NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Every exception the package throws for a call it refuses extends TagsException (a RuntimeException). Present them through four stable methods rather than knowing each class: errorCode() — a snake_case code, public API from 1.0; status() — the HTTP status for a reference in a request body; isNotFound() — the not-found family, which answers 404 when the reference was a path segment; clientMessage() — the translated text a client may see. Messages come from tags::messages in English and Slovak and never name a PHP class:

use RoundlyConsulting\Tags\Exceptions\TagsException;

try {
    $concert->attachTags($request->input('labels', []), 'label');
} catch (TagsException $e) {
    return response()->json(
        ['message' => $e->clientMessage(), 'code' => $e->errorCode()],
        $e->status(),
    );
}
ExceptionerrorCode()status()When
UnknownTagTypeExceptionunknown_tag_type / type_required404 / 422A type nobody defined (or one hidden from the caller); a string input with no type while there is no default type.
TagNotFoundExceptiontag_not_found422 (404 in the path)A key, slug or name that matches no live tag the caller may use; a deleted tag.
TagScopeMismatchExceptiontag_not_found422 (404 in the path)A tag of another scope — presents exactly like a missing one; getMessage() keeps the precise text for logs.
DuplicateTagExceptionduplicate_tag409A name or manual slug whose slug another tag of the type and scope holds — names the slug and the locale, and points to restore() when a deleted tag holds it.
InvalidTagExceptioninvalid_tag422A blank, too long or unsluggable name, an unsupported or malformed locale, a bad colour, icon, description, slug or position, a manual slug on a free-form type, a scope on a global-only type, a change to type or owner.
InvalidTagHierarchyExceptioninvalid_hierarchy422A cycle, a tree deeper than the type allows, a parent on a flat type, a deleted parent.
TagHasChildrenExceptiontag_has_children409A delete that would orphan live children — pass TagChildren::Reparent or Delete.
TagLimitExceededExceptiontag_limit_exceeded422A write that would leave a model with more tags of a type than allowed; nothing is written.
TagTypeMismatchExceptiontag_type_mismatch422A tag of one type used where another was asked for — a tagging call, a parent, a handle.
TagTypeNotAllowedExceptiontag_type_not_allowed422The type’s on() list or the model’s tagTypes() leaves the other out.
UnknownMorphAliasExceptionunknown_alias422An API scope or taggable reference the allow-lists do not hold — or a scope the caller may not use, which answers the same.
InvalidTagTypeExceptioninvalid_tag_type500A type definition that cannot work (see Tag types).
TagTypeAlreadyDefinedExceptiontype_already_defined500A second definition of one name, naming both definers.
UnsavedTaggableExceptionunsaved_taggable500Tagging a model that is not saved yet.
  • A missing key presents to clients exactly like a tag of another scope: clientMessage() is the generic “The tag does not exist.” while getMessage() keeps the key for your logs.
  • Outside the family: InvalidTagsConfigurationException (configuration the package cannot work with, named by its key — it extends the toolkit’s exception, so catch it beside InvalidConfigurationException) and ReadOnlyTagsRelationException (a LogicException for a write through tags()).

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

By donating, you agree to our donation terms.

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.