laravel/framework · error · BroadcastException

Pusher error: .

Error message

Pusher error: %s.

What it means

PusherBroadcaster::broadcast wraps the Pusher SDK's trigger() call in a try/catch and re-throws any Pusher\ApiErrorException as a BroadcastException with a descriptive message. The '%s' is the original Pusher SDK message. This is the single chokepoint through which all Pusher transport-level failures surface to application code.

Solutions

  1. Verify PUSHER_APP_KEY, PUSHER_APP_SECRET, PUSHER_APP_ID, and PUSHER_APP_CLUSTER in .env match the Pusher dashboard values.
  2. Reduce the broadcast payload size by selecting only needed attributes on models (e.g., $model->only(['id','name'])) and avoid serializing relations.
  3. Catch \Illuminate\Broadcasting\BroadcastException around broadcast() / event() calls and log/queue a retry for transient Pusher failures.
  4. Validate channel names against Pusher's allowed pattern (max 200 chars, alphanumeric plus _-=,.@) before broadcasting.

Example fix

// before
broadcast(new OrderShipped($order));

// after
try {
    broadcast(new OrderShipped($order));
} catch (\Illuminate\Broadcasting\BroadcastException $e) {
    logger()->error('Pusher broadcast failed', ['exception' => $e, 'order_id' => $order->id]);
    throw $e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate payload size and channel names before broadcasting
$payload = (new \Illuminate\Broadcasting\BroadcastEvent($event))->payload;
if (strlen(json_encode($payload)) > 10_000) {
    throw new \RuntimeException('Pusher payload exceeds 10KB limit');
}
foreach ($channels as $channel) {
    if (! preg_match('/^[a-zA-Z0-9_\-=,.@:]{1,200}$/', $channel)) {
        throw new \RuntimeException("Invalid Pusher channel name: {$channel}");
    }
}

Try / catch

use Illuminate\Broadcasting\BroadcastException;

try {
    broadcast($event);
} catch (BroadcastException $e) {
    if (str_contains($e->getMessage(), 'Pusher error')) {
        // log Pusher-specific failure, surface alert
        report($e);
        return;
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling event() or broadcast() that targets a Pusher-backed broadcast connection while: Pusher credentials (app key/secret/cluster) are wrong, the channel name violates Pusher naming rules, the payload exceeds Pusher size limits (10KB), the request hits Pusher rate limits, or the network cannot reach the Pusher API.

Common situations: Wrong PUSHER_APP_CLUSTER env value (e.g., 'mt1' vs 'us2'). Mismatched PUSHER_APP_KEY/PUSHER_APP_SECRET between environments. Payload containing large unserialized Eloquent models. Exceeding 100 messages per chunk boundary with malformed channel names. Staging credentials leaking into production.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/1759edaab0940481. Report an issue: GitHub.

Appendix: source

Thrown at src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php:171

     * @param  array  $payload
     * @return void
     *
     * @throws \Illuminate\Broadcasting\BroadcastException
     */
    public function broadcast(array $channels, $event, array $payload = [])
    {
        $socket = Arr::pull($payload, 'socket');

        $parameters = $socket !== null ? ['socket_id' => $socket] : [];

        $channels = new Collection($this->formatChannels($channels));

        try {
            $channels->chunk(100)->each(function ($channels) use ($event, $payload, $parameters) {
                $this->pusher->trigger($channels->toArray(), $event, $payload, $parameters);
            });
        } catch (ApiErrorException $e) {
            throw new BroadcastException(
                sprintf('Pusher error: %s.', $e->getMessage())
            );
        }
    }

    /**
     * Get the Pusher SDK instance.
     *
     * @return \Pusher\Pusher
     */
    public function getPusher()
    {
        return $this->pusher;
    }

    /**
     * Set the Pusher SDK instance.
     *

View on GitHub (pinned to e0f6eb3518)