w7corp/easywechat · error · RuntimeException

No suite_ticket found.

Error message

No suite_ticket found.

What it means

Thrown by SuiteTicket::getTicket() when the cache lookup for the stored suite_ticket returns nothing or a non-string. WeChat pushes a fresh suite_ticket to your app every ~10 minutes via the OpenWork callback; this error means no ticket has ever been stored (or it was evicted) in the cache the library reads.

Source

Thrown at src/OpenWork/SuiteTicket.php:55

        return $this;
    }

    public function setTicket(string $ticket): static
    {
        $this->cache->set($this->getKey(), $ticket, 6000);

        return $this;
    }

    /**
     * @throws RuntimeException
     */
    public function getTicket(): string
    {
        $ticket = $this->cache->get($this->getKey());

        if (! $ticket || ! is_string($ticket)) {
            throw new RuntimeException('No suite_ticket found.');
        }

        return $ticket;
    }
}

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Wire the OpenWork message callback and persist the ticket in handle('suite_ticket', ...) so pushes land in the same cache the app reads
  2. Deploy/hit the callback once and wait up to 10 minutes for the next push, then retry
  3. Point both the callback handler and API code at the same persistent cache (Redis) and verify the cache key with getKey()
  4. As a stopgap for testing, seed the ticket manually via SuiteTicket::setTicket()

Example fix

// before
$openWork->suite_access_token->getToken(); // RuntimeException: No suite_ticket found.
// after - make sure callback saves it and share the cache
$openWork->server->handle('suite_ticket', fn ($message, \EasyWeChat\OpenWork\SuiteTicket $ticket) => $ticket->setTicket($message->SuiteTicket));
// ... after next push (<=10 min):
$openWork->suite_access_token->getToken();
Defensive patterns

Strategy: retry

Validate before calling

$hasTicket = (static function () use ($suiteTicket) {
    try { $suiteTicket->getTicket(); return true; }
    catch (\EasyWeChat\Kernel\Exceptions\RuntimeException) { return false; }
})();
if (! $hasTicket) { /* defer job ~10 min or fail gracefully */ }

Try / catch

use EasyWeChat\Kernel\Exceptions\RuntimeException;
try {
    $ticket = $suiteTicket->getTicket();
} catch (RuntimeException $e) {
    if ($e->getMessage() === 'No suite_ticket found.') {
        // ticket push pending: reschedule, don't crash the job
        $this->release(600);
        return;
    }
    throw $e;
}

Prevention

When it happens

Trigger: Any code path that resolves suite_access_token (SuiteAccessToken::refresh() pulls suiteTicket?->getTicket()) before a ticket was ever cached: fresh deployments, cache flush/restart with volatile cache (array/file), callback endpoint never invoked, or callback handled by a different process with a different cache store.

Common situations: New environment where the ticket push callback is not yet wired; local development where the push URL points at production but local code asks for a ticket; using Redis cache in callback but array cache in worker; ticket key evicted by short TTL right before get_suite_token runs.

Related errors


AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21). Data as JSON: /api/errors/28d19a54f5dfa750. Report an issue: GitHub.