laravel/framework · error · InvalidArgumentException
Callback must be a callable, callback array, or a 'Class@met
Error message
Callback must be a callable, callback array, or a 'Class@method' string.
What it means
Thrown by Gate::define() when the supplied $callback is neither a PHP callable, an array whose first element is a class string, nor a 'Class@method' string. The Gate rejects any other type so that ability resolution never silently stores an unusable handler.
Source
Thrown at src/Illuminate/Auth/Access/Gate.php:212
*
* @throws \InvalidArgumentException
*/
public function define($ability, $callback)
{
$ability = enum_value($ability);
if (is_array($callback) && isset($callback[0]) && is_string($callback[0])) {
$callback = $callback[0].'@'.$callback[1];
}
if (is_callable($callback)) {
$this->abilities[$ability] = $callback;
} elseif (is_string($callback)) {
$this->stringCallbacks[$ability] = $callback;
$this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback);
} else {
throw new InvalidArgumentException("Callback must be a callable, callback array, or a 'Class@method' string.");
}
return $this;
}
/**
* Define abilities for a resource.
*
* @param string $name
* @param string $class
* @param array|null $abilities
* @return $this
*/
public function resource($name, $class, ?array $abilities = null)
{
$abilities = $abilities ?: [
'viewAny' => 'viewAny',
'view' => 'view',View on GitHub (pinned to bd6b5437e6)
Solutions
- Verify the callback is a Closure/callable, a ['Class','method'] array, or a 'Class@method' string before calling define().
- If referencing a class string, confirm the class exists and the method is public.
- Use Gate::policy() or a policy class mapping instead of manual define() when authorizing models.
- Add a unit test that asserts is_callable($callback) (or Str::parseCallback validity) for dynamic registrations.
Example fix
// before
Gate::define('update-post', $maybeNull);
// after
use Illuminate\Support\Str;
if (is_callable($maybeNull) || Str::parseCallback($maybeNull) !== [null, null]) {
Gate::define('update-post', $maybeNull);
} Defensive patterns
Strategy: validation
Validate before calling
use Illuminate\Support\Str;
function isValidGateCallback($callback): bool
{
return is_callable($callback)
|| (is_string($callback) && Str::parseCallback($callback) !== [null, null])
|| (is_array($callback) && isset($callback[0], $callback[1]) && is_string($callback[0]));
}
if (! isValidGateCallback($cb)) {
throw new InvalidArgumentException('Refusing to define ability with invalid callback.');
} Type guard
function isGateCallback(mixed $callback): bool
{
return is_callable($callback) || is_string($callback);
} Try / catch
try {
Gate::define('update-post', $callback);
} catch (\InvalidArgumentException $e) {
// log which ability/callback failed registration and fall back to a policy
logger()->error('Invalid Gate callback', ['message' => $e->getMessage()]);
} Prevention
- Validate dynamic callbacks with is_callable()/Str::parseCallback before calling Gate::define().
- Prefer policy classes over manual define() for model authorization.
- Cover dynamic ability registration with a unit test asserting no InvalidArgumentException.
When it happens
Trigger: Calling Gate::define('update', $someValue) where $someValue is null, an integer, an object that is not callable, or a malformed array like ['Class', 123]; also triggered indirectly through Gate::resource() when the class argument is not a string.
Common situations: Passing a non-existent class@method string that resolves to a non-callable, passing null when a policy class was expected, or a typo where a variable holding a closure is accidentally overwritten before define().
Related errors
- Auth guard [{$name}] is not defined.
- Auth driver [{$config['driver']}] for guard [{$name}] is not
- Authentication user provider [{$driver}] is not defined.
- Password resetter [{$name}] is not defined.
- Cookie jar has not been set.
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/443e157e9176ad8b.json.
Report an issue: GitHub.