{"id":"cbd7e35c776e267b","repo":"laravel/framework","slug":"attempted-to-batch-job-s-but-it-does-not-use-t","errorCode":null,"errorMessage":"Attempted to batch job [%s], but it does not use the Batchable trait.","messagePattern":"Attempted to batch job \\[(.+?)\\], but it does not use the Batchable trait\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Illuminate/Bus/PendingBatch.php","lineNumber":110,"sourceCode":"    /**\n     * Ensure the given job is batchable.\n     *\n     * @param  object|array  $job\n     * @return void\n     *\n     * @throws \\RuntimeException\n     */\n    protected function ensureJobIsBatchable(object|array $job): void\n    {\n        foreach (Arr::wrap($job) as $job) {\n            if ($job instanceof PendingBatch || $job instanceof Closure) {\n                return;\n            }\n\n            if (! (static::$batchableClasses[$job::class] ?? false) && ! isset(class_uses_recursive($job)[Batchable::class])) {\n                static::$batchableClasses[$job::class] = false;\n\n                throw new RuntimeException(sprintf('Attempted to batch job [%s], but it does not use the Batchable trait.', $job::class));\n            }\n\n            static::$batchableClasses[$job::class] = true;\n        }\n    }\n\n    /**\n     * Add a callback to be executed when the batch is stored.\n     *\n     * @param  callable  $callback\n     * @return $this\n     */\n    public function before($callback)\n    {\n        $this->registerCallback('before', $callback);\n\n        return $this;\n    }","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/laravel/framework/blob/bd6b5437e6ad87bb49f9b426724f07a9f64e9683/src/Illuminate/Bus/PendingBatch.php#L92-L128","documentation":"PendingBatch::__construct()/add() calls ensureJobIsBatchable(), which uses class_uses_recursive() to verify each job uses the Illuminate\\Bus\\Batchable trait. Jobs in a batch must be Batchable so the framework can attach the batch ID and report progress. A missing trait throws this RuntimeException immediately.","triggerScenarios":"Calling Bus::batch([new MyJob()])->dispatch() where MyJob extends a queueable base but does not 'use Batchable;'. Passing a plain object job into Bus::batch() or PendingBatch::add().","commonSituations":"Adding an existing queued job to a new batch without retrofitting the trait. Copying a job class that predates job batching. Forgetting the trait after scaffolding with make:job.","solutions":["Add 'use \\Illuminate\\Bus\\Batchable;' inside the job class.","Inside the job's handle(), use $this->batch() to access the batch (only available with the trait).","If batching closures or nested PendingBatch instances, note those are exempt; do not pass raw non-Batchable objects."],"exampleFix":"// before\nclass ProcessPodcast implements ShouldQueue\n{\n    public function handle() { ... }\n}\nBus::batch([new ProcessPodcast()])->dispatch();\n\n// after\nuse Illuminate\\Bus\\Batchable;\n\nclass ProcessPodcast implements ShouldQueue\n{\n    use Batchable;\n\n    public function handle()\n    {\n        $batch = $this->batch();\n    }\n}\nBus::batch([new ProcessPodcast()])->dispatch();","handlingStrategy":"type-guard","validationCode":"foreach ($jobs as $job) {\n    if (! in_array(\\Illuminate\\Bus\\Batchable::class, class_uses_recursive($job), true)) {\n        throw new \\LogicException(get_class($job).' must use the Batchable trait to be batched.');\n    }\n}\nBus::batch($jobs)->dispatch();","typeGuard":"function isBatchable(object $job): bool\n{\n    return isset(class_uses_recursive($job)[\\Illuminate\\Bus\\Batchable::class]);\n}\n\n$batchable = array_filter($jobs, 'isBatchable');","tryCatchPattern":"try {\n    Bus::batch($jobs)->dispatch();\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'does not use the Batchable trait')) {\n        // add the trait to the offending class and retry\n    }\n    throw $e;\n}","preventionTips":["Make your base job class use Batchable.","Run a static analysis rule that all classes passed to Bus::batch use Batchable.","Document batchable job requirements in the project's job conventions."],"tags":["bus","batching","queue","traits"],"analyzedSha":"bd6b5437e6ad87bb49f9b426724f07a9f64e9683","analyzedAt":"2026-08-06T00:28:32.783Z","schemaVersion":2}