laravel/framework · error · InvalidArgumentException

Invalid scheduled callback event. Must be a string or callab

Error message

Invalid scheduled callback event. Must be a string or callable.

What it means

Thrown by CallbackEvent::__construct() (src/Illuminate/Console/Scheduling/CallbackEvent.php:55) when the callback is neither a string nor something Reflector::isCallable() accepts. The scheduler wraps user callbacks in CallbackEvent and refuses values it cannot later invoke via the container.

Source

Thrown at src/Illuminate/Console/Scheduling/CallbackEvent.php:55

     *
     * @var \Throwable|null
     */
    protected $exception;

    /**
     * Create a new event instance.
     *
     * @param  \Illuminate\Console\Scheduling\EventMutex  $mutex
     * @param  string|callable  $callback
     * @param  array  $parameters
     * @param  \DateTimeZone|string|null  $timezone
     *
     * @throws \InvalidArgumentException
     */
    public function __construct(EventMutex $mutex, $callback, array $parameters = [], $timezone = null)
    {
        if (! is_string($callback) && ! Reflector::isCallable($callback)) {
            throw new InvalidArgumentException(
                'Invalid scheduled callback event. Must be a string or callable.'
            );
        }

        $this->mutex = $mutex;
        $this->callback = $callback;
        $this->parameters = $parameters;
        $this->timezone = $timezone;
    }

    /**
     * Run the callback event.
     *
     * @param  \Illuminate\Contracts\Container\Container  $container
     * @return mixed
     *
     * @throws \Throwable
     */

View on GitHub (pinned to bd6b5437e6)

Solutions

  1. Pass a Closure: Schedule::call(fn () => doWork()).
  2. Pass a callable string 'Class@method' or 'Class::method' that the container can resolve.
  3. Pass an invokable object (instance with __invoke).
  4. For job dispatch use Schedule::job(SomeJob::class) rather than Schedule::call(SomeJob::class) when the class isn't invokable.

Example fix

// before
$schedule->call(ReportGenerator::class)->daily();  // not invokable

// after
$schedule->call(fn () => app(ReportGenerator::class)->run())->daily();
// or
$schedule->job(new ReportGenerator)->daily();
Defensive patterns

Strategy: type-guard

Validate before calling

use Illuminate\Support\Reflector;
if (! is_string($cb) && ! Reflector::isCallable($cb)) {
    // convert to a Closure or invokable object before Schedule::call()
}

Type guard

use Illuminate\Support\Reflector;
function isValidCallback($cb): bool
{
    return is_string($cb) || Reflector::isCallable($cb);
}

Try / catch

try {
    $schedule->call($cb)->daily();
} catch (\InvalidArgumentException $e) {
    $schedule->call(fn () => null)->daily(); // safe fallback
}

Prevention

When it happens

Trigger: Schedule::call($x) where $x is an array that isn't a valid callable array, an object without __invoke, a non-existent class/method string, or a plain scalar. Schedule::job() or other API misuse that ultimately constructs a CallbackEvent with an invalid callback.

Common situations: Passing a class-name string of a non-invokable class, passing an array ['Class', 'method'] where the method doesn't exist, or accidentally passing null/false from a bad variable.

Related errors


AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06). Data as JSON: /data/errors/5691f6a77f66f891.json. Report an issue: GitHub.