Errors & exceptions
Warrant fails loudly. A typo in a stored rule, a missing context key, or a misconfigured schema throws rather than silently granting or denying. This is the catalogue.
When each error surfaces
Section titled “When each error surfaces”| Stage | What’s checked |
|---|---|
| Parse time | Rule syntax, binding consistency (WarrantSyntaxException) |
| Compile / validate time | Ability / condition names exist on the schema |
| Check time | Requested ability exists; required context present; user available |
| Boot / reflection | Schema registry uniqueness; condition method signatures |
Syntax errors → WarrantSyntaxException
Section titled “Syntax errors → WarrantSyntaxException”Thrown eagerly from the lexer/parser. Extends RuntimeException and carries
$source, $offset, $sourceLine, and $sourceColumn. The message includes the
line, column, and a caret:
Reserved word 'can' cannot be used as a name; expected an ability name. (line 1, column 21)
if is_self they can can ^Representative messages:
Unexpected character %s.Unterminated string literal.Invalid escape sequence "\%s"; only \', \", and \\ are allowed.Expected 'context' after '@'./Expected a context key after '@context'.Expected 'can' or 'cannot' after 'they'.Expected at least one 'they can ...' or 'they cannot ...' clause.Expected ')' to close the group./Expected ')' to close the condition arguments.Reserved word '%s' cannot be used as a name; expected %s.Expected a rule./Expected a single rule but found multiple.
Binding errors (also WarrantSyntaxException)
Section titled “Binding errors (also WarrantSyntaxException)”Cannot mix named and positional bindings.No binding provided for ":%s".More positional placeholders (?) than bindings provided.%d positional binding(s) were provided but never used.Binding(s) provided but never used: %s.
Validation errors → InvalidArgumentException
Section titled “Validation errors → InvalidArgumentException”Thrown when a rule set is validated/compiled against a schema:
Ability [%s] is not declared by the schema.Condition [%s] is not declared by the schema.Condition [%s] requires at least %d argument(s), but the rule supplied %d.
Context keys need no declaration to be referenced in a rule (@context <key>)
or read in a condition, so there is no “unknown context key” validation error.
Attaching a denial message to a rule that has no theyCannot clause is also
rejected here — only a cannot rule may carry one, whether it was set with
withDenialMessage() or written in the DSL with because. See
Denial messages.
fromRules / validateAll type-guard their inputs:
fromRules expects WarrantRule or WarrantRuleBuilder instances, got %s.validateAll expects WarrantRuleSet instances, got %s.
Condition / reflection errors → InvalidArgumentException
Section titled “Condition / reflection errors → InvalidArgumentException”Thrown lazily the first time a schema’s conditions are reflected:
Condition method [%s::%s] must not declare duplicate condition attributes.Condition method [%s::%s] cannot declare both #[RowCondition] and #[GlobalCondition].Condition method [%s::%s] must resolve to a non-empty condition key.Condition method [%s::%s] must accept a [%s] as its first parameter.— a missing or wrong-typed context parameter.Schema [%s] has no rows and does not support targeted checks; use a no-target check instead.Schema [%s] has rows but no way to name one, so it does not support targeted checks; declareconst keyfor the column its rows are identified by, or a matchKey() of its own. Filtering a query and selecting per-row abilities need neither.
Applying a condition
Section titled “Applying a condition”From the condition resolver:
BadMethodCallException—Condition [%s] is not defined on schema [%s].InvalidArgumentException—Condition [%s] on schema [%s] requires a target row.(a row condition run with no target)InvalidArgumentException—Condition [%s] on schema [%s] requires at least %d argument(s), but the rule supplied %d.(fewer arguments than the condition’s required parameters)
From the compiler, on what a condition emitted:
InvalidArgumentException—Condition [%s] on schema [%s] may only add where clauses, but it emitted a [%s]; ...(ajoin,groupBy,having, aggregate, orunion— none of which can be spliced into anORor negated in place)InvalidArgumentException—Condition [%s] on schema [%s] added no where clause; a condition must add at least one where clause, return true/false to decide the outcome outright, or return null to answer unknown.(a condition that returned its query untouched — see How it compiles)InvalidArgumentException—Condition [%s] on schema [%s] returned null, answering unknown, but also added a where clause; return the builder it constrained, or answer unknown without constraining it.(almost always a missingreturn— see Answering unknown)
Row keys → InvalidArgumentException
Section titled “Row keys → InvalidArgumentException”From validation, on a handle against the target schema’s row key — and from the compiler, which makes the same checks for handles that never pass through the parser:
A %s(...) reference targets a specific row of schema [%s], but [%s] has no way to name one; declare \const key` for the column its rows are identified by, or a matchKey() of its own, or drop the row selector.`A %s(...) reference to schema [%s] supplies %d row-key argument(s), but that schema's row key requires at least %d.
From the key’s own dispatch:
The row key for schema [%s] requires at least %d argument(s), but %d were supplied.The row key for schema [%s] must return the query it constrained, or null to answer unknown; it returned a [%s].
From the schema’s own declaration:
Schema [%s] declares a condition attribute on matchKey(), which is the schema's row key and not part of its rule vocabulary; remove the attribute, or move the logic to a condition method of its own.Schema [%s] must accept a [%s] as the first parameter of matchKey().
From a schema declaring both row sources, on first resolution:
Schema [%s] names model [%s] and also defines a virtualTable(); a schema draws its rows from one or the other. Drop the model to make it a virtual table, or drop virtualTable() to keep the model's own table.Schema [%s] names model [%s] and also declares a key [%s]; a model answers for its own key, so drop the constant. It is for a virtual table, whose rows have no key of their own.
From a condition or key over rows with no key column of their own:
These rows have no key column of their own, so row() must be given a column name; rows drawn from a virtualTable() need a matchKey() that names its own columns.(BadMethodCallException)
An empty argument list is not an error in itself: it addresses a row by a key
that requires no arguments, exactly as schema() does in rule text. It is the
arity message above that rejects it against a key which does require some. Only
null is a no-target check.
Cross-schema row selectors → InvalidArgumentException
Section titled “Cross-schema row selectors → InvalidArgumentException”From the compiler, on the value inside a can(... for schema(<row>)) or
check(... for schema(<row>)) handle. Both replace what used to be a query that
silently matched no row:
The row selector for schema [%s] is a [%s], which is not that schema's model [%s]; pass that schema's own model or a row key.The row selector for schema [%s] is a [%s], which cannot identify a row; pass a key, that schema's model, or a @column/@sql reference.
See What a row selector may be for the accepted values.
Context errors → InvalidArgumentException
Section titled “Context errors → InvalidArgumentException”Schema [%s] requires context key(s) [%s]; supply them at the check or via defaultContext().See Check-time context. Note that an
optional key that’s absent doesn’t
throw — it’s passed to its condition as null (standard SQL logic then applies,
which is fail-closed).
Registry errors → SchemaRegistry
Section titled “Registry errors → SchemaRegistry”InvalidArgumentException—Schema [...] is registered under more than one schema key [...](when the index is first built from the container)OutOfBoundsException—No Warrant %s registered for reference [%s].— where%sis the coordinate being resolved (schemaormodel) and the reference is the class name or key that failed to resolve (e.g.No Warrant schema registered for reference [documents].)
These fire the first time a schema is resolved, not at boot — checking any of them requires loading the schema class, which is exactly what the index defers:
LogicException—Schema key [...] is registered to [...], which is not a Warrant\Schema\WarrantSchema.LogicException—Schema [...] names model [...], which is not an Eloquent model.LogicException—Schema [...] names model [...], but that model does not use the Warrant\HasWarrantSchema trait, ...LogicException—Model [...] must declare warrantSchema() as \public static`.`LogicException—Schema [...] names model [...], but that model names schema [...]; a schema and its model must name each other.
The same pair is checked from the model end when the reference is a model — a
row check, a query scope, or loadUserAbilities(). That direction is the one that
catches a subclass inheriting warrantSchema() from its parent:
LogicException—Model [...] must name a Warrant\Schema\WarrantSchema, but names [...].LogicException—Model [...] names schema [...], but that schema names model [...]; a schema and its model must name each other.
Authorization failures → WarrantAuthorizationException
Section titled “Authorization failures → WarrantAuthorizationException”Thrown by authorize() / authorizeAny()
when a check is denied. It extends Illuminate\Auth\Access\AuthorizationException, so Laravel
renders it as HTTP 403 automatically.
public function __construct( string $message = 'This action is unauthorized.', ?WarrantDenialContext $denial = null,);
public readonly ?WarrantDenialContext $denial; // the diagnosed denial, or null for a generic denialThe $denial property carries a diagnosed denial-context data object (a plain
final readonly object under Warrant\, not an exception) describing why the
check failed:
WarrantGate— the requestedarray $abilities(normalized, wildcards resolved) andAbilityMatchMode $matchMode.WarrantDenialContext—$user,?Model $target,string $schema,array $context,WarrantGate $gate, the responsibleWarrantRule $rule(the matchingcannot), andarray $deniedAbilities.WarrantUngrantedContext— same fields minus$rule, witharray $ungrantedAbilitiesin place ofdeniedAbilities(the whole gate underANY; the missing subset underALL).
See Denial messages for attaching messages and the schema fallback hooks.
Middleware errors
Section titled “Middleware errors”See Middleware API. Unauthenticated or
unauthorized requests throw
WarrantAuthorizationException
(rendered as 403); misconfiguration throws InvalidArgumentException. The
warrant: gate and the reachability guards use distinct message strings — see
the Middleware API errors table.
“No authenticated user” → depends on the entry point
Section titled ““No authenticated user” → depends on the entry point”When no user is passed and none is authenticated, the failure surfaces two ways:
InvalidArgumentException—Warrant requires an authenticated user or an explicit user instance.— from the engine entry points (Warrant::guard(),Warrant::forSchema(), and every facade check / reachability helper that resolves the current user).LogicException— from the query scopes / instance helpers that need a user but weren’t given one (scopeUserHasAbility,scopeSelectUserAbilities,loadUserAbilities).
Writer errors → LogicException
Section titled “Writer errors → LogicException”Thrown by toSyntax() when a rule can’t be rendered as inline DSL — use
toBoundSyntax() instead:
A constant boolean expression has no rule-language representation.Condition parameter of type %s cannot be written inline; use toBoundSyntax().NAN/INF cannot be written inline; use toBoundSyntax().Float %s requires exponent notation, unsupported inline; use toBoundSyntax().
Configuration & driver errors → RuntimeException
Section titled “Configuration & driver errors → RuntimeException”No Warrant rule resolver configured. Set warrant.rule_resolver to a class implementing Warrant\Rules\RuleResolver.Warrant ability selection does not support the [%s] database driver.(a driver other than PostgreSQL, MySQL/MariaDB, or SQLite for the per-row abilities column)
