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

Every exception lives in RoundlyConsulting\Teams\Exceptions and extends TeamsException, which extends RuntimeException. Catch the base type for any package error, or a specific one for targeted handling:

ExceptionThrown byWhen
InviteExpiredExceptionAcceptInviteActionThe invite’s expires_at has passed.
InviteExhaustedExceptionAcceptInviteActionuses has reached max_uses — including a single-use invite already consumed.
InviteEmailMismatchExceptionAcceptInviteActionAn email-targeted invite gets a different or missing email (compared case-insensitively).
InviteNotFoundExceptionTeams::invites()->accept()No invite matches the code, the invite was revoked, or its team was deleted.
InviteNotFoundExceptionfor($t)->invites()->resend() / revoke()The invite belongs to another team.
JoinRequestNotFoundExceptionfor($t)->joinRequests()->approve() / deny()The request belongs to another team.
JoinRequestNotPendingExceptionfor($t)->joinRequests()->approve() / deny()The request was already approved, denied or expired — including by a concurrent responder.
MemberNotFoundExceptionfor($t)->members()->changeRole()The model is not a member of the team.
TeamsExceptionjoinRequests()->open(), members()->add(), transferOwnershipTo()The base class. Thrown directly for an invite-only team’s join request, a join request from an active member, and a reached seat limit.
use RoundlyConsulting\Teams\Exceptions\InviteEmailMismatchException;
use RoundlyConsulting\Teams\Exceptions\InviteExhaustedException;
use RoundlyConsulting\Teams\Exceptions\InviteExpiredException;
use RoundlyConsulting\Teams\Exceptions\InviteNotFoundException;
use RoundlyConsulting\Teams\Exceptions\TeamsException;

try {
    Teams::invites()->accept($code, $user, email: $user->email);
} catch (InviteExpiredException) {
    // the link has expired
} catch (InviteExhaustedException) {
    // no seats left, or a single-use link already consumed
} catch (InviteEmailMismatchException) {
    // the wrong recipient
} catch (InviteNotFoundException) {
    // an unknown code, a revoked invite or a deleted team
} catch (TeamsException $e) {
    // any other teams error — e.g. a seat limit
}

The invite checks run expired → exhausted → revoked → email mismatch → deleted team, so the first failing condition is the one you catch.

Translated messages

The invite, join-request, membership, seat-limit and interval-config messages come from the teams:: translation namespace and follow the app locale — English and Slovak ship:

KeyEnglish default
teams::errors.already_memberThe given model is already a member of team #:team.
teams::errors.invite_expiredThe invite ":code" has expired.
teams::errors.invite_email_mismatchThis invite is addressed to ":email".
teams::errors.invite_exhaustedThe invite ":code" has reached its usage limit.
teams::errors.invite_not_foundNo invite was found for the code ":code".
teams::errors.invite_team_missingThe team of invite #:id no longer exists.
teams::errors.invite_unavailableInvite #:id is no longer available.
teams::errors.invite_not_in_teamInvite #:id does not belong to this team.
teams::errors.join_request_not_pendingJoin request #:id has already been resolved.
teams::errors.join_request_not_in_teamJoin request #:id does not belong to this team.
teams::errors.member_not_foundThe given model is not a member of team #:team.
teams::errors.join_policy_forbids_requestsThis team is invite-only and does not accept join requests.
teams::errors.invalid_config_valueConfiguration value [:key] must be :expected, [:given] given.
teams::errors.config_expectations.positive_intervala positive interval such as "7 days"
teams::errors.config_expectations.non_negative_intervala zero or positive interval such as "30 days"
teams::errors.max_seats_reachedThis team has reached its seat limit (:count seat).|This team has reached its seat limit (:count seats).

max_seats_reached is rendered with trans_choice(): English uses singular|plural, Slovak {0} …|{1} …|[2,4] …|[5,*] …. An override may keep a single line without | — it is rendered as is.

php artisan vendor:publish --tag="teams-translations"
# edit lang/vendor/teams/en/errors.php or lang/vendor/teams/sk/errors.php, or add other locales

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.