{"record":{"id":"443e157e9176ad8b","repo":"laravel/framework","slug":"callback-must-be-a-callable-callback-array-or-a","errorCode":null,"errorMessage":"Callback must be a callable, callback array, or a 'Class@method' string.","messagePattern":"Callback must be a callable, callback array, or a 'Class@method' string\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Auth/Access/Gate.php","lineNumber":212,"sourceCode":"     *\n     * @throws \\InvalidArgumentException\n     */\n    public function define($ability, $callback)\n    {\n        $ability = enum_value($ability);\n\n        if (is_array($callback) && isset($callback[0]) && is_string($callback[0])) {\n            $callback = $callback[0].'@'.$callback[1];\n        }\n\n        if (is_callable($callback)) {\n            $this->abilities[$ability] = $callback;\n        } elseif (is_string($callback)) {\n            $this->stringCallbacks[$ability] = $callback;\n\n            $this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback);\n        } else {\n            throw new InvalidArgumentException(\"Callback must be a callable, callback array, or a 'Class@method' string.\");\n        }\n\n        return $this;\n    }\n\n    /**\n     * Define abilities for a resource.\n     *\n     * @param  string  $name\n     * @param  string  $class\n     * @param  array|null  $abilities\n     * @return $this\n     */\n    public function resource($name, $class, ?array $abilities = null)\n    {\n        $abilities = $abilities ?: [\n            'viewAny' => 'viewAny',\n            'view' => 'view',","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/laravel/framework/blob/e0f6eb3518ac29fbbca8529e97d0df7fc9f24481/src/Illuminate/Auth/Access/Gate.php#L194-L230","documentation":"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 normalizes array callbacks to 'Class@method', stores string callbacks for lazy resolution, and callable values directly — anything else is rejected as a misconfiguration of an ability.","triggerScenarios":"Calling Gate::define($ability, $callback) (or Authorizable::define / Auth::define via the gate facade) with a value such as null, an integer, an object that is not __invoke-able, or an array lacking a string class key.","commonSituations":"Passing a non-static method array ['SomeClass', 'method'] where the class entry was overwritten; passing a service-container-resolved instance by mistake instead of a class string; typos in 'Class@method' that produce a non-string after concatenation; passing null from an optional config value.","solutions":["Pass a closure: Gate::define('update-post', fn (User $user, Post $post) => $user->id === $post->user_id);","Pass a 'Class@method' string: Gate::define('update-post', 'PostPolicy@update');","Pass a callable array with a class string first element: Gate::define('update-post', [PostPolicy::class, 'update']);","Inspect the $callback variable at the call site to confirm its runtime type before calling define()."],"exampleFix":"// before\nGate::define('update-post', $policyInstance); // an object, not invokable\n\n// after\nGate::define('update-post', [PostPolicy::class, 'update']);\n// or\nGate::define('update-post', PostPolicy::class . '@update');","handlingStrategy":"validation","validationCode":"// PHP — validate before Gate::define\nfunction isValidGateCallback($callback): bool {\n    if (is_callable($callback)) return true;\n    if (is_string($callback)) return true;\n    if (is_array($callback) && isset($callback[0]) && is_string($callback[0])) return true;\n    return false;\n}\nif (! isValidGateCallback($cb)) {\n    throw new \\InvalidArgumentException('Invalid gate callback supplied for ability.');\n}\nGate::define($ability, $cb);","typeGuard":"function isValidGateCallback($callback): bool {\n    return is_callable($callback)\n        || is_string($callback)\n        || (is_array($callback) && isset($callback[0]) && is_string($callback[0]));\n}","tryCatchPattern":"try {\n    Gate::define($ability, $callback);\n} catch (\\InvalidArgumentException $e) {\n    // log the ability + callback type for diagnosis\n    report(['ability' => $ability, 'type' => get_debug_type($callback), 'err' => $e->getMessage()]);\n    throw $e;\n}","preventionTips":["Prefer closures or Class@method strings when defining abilities.","Validate callback shape in config loaders before passing to Gate::define.","Add static analysis (PHPStan) to ensure define() receives the expected callback type."],"tags":["authorization","gate","laravel","configuration"],"backgroundTag":null,"analyzedSha":"e0f6eb3518ac29fbbca8529e97d0df7fc9f24481","analyzedAt":"2026-08-11T20:52:37.562Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}