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
- Wire the OpenWork message callback and persist the ticket in handle('suite_ticket', ...) so pushes land in the same cache the app reads
- Deploy/hit the callback once and wait up to 10 minutes for the next push, then retry
- Point both the callback handler and API code at the same persistent cache (Redis) and verify the cache key with getKey()
- 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
- Wire the suite_ticket callback handler from day one and verify it returns 'success'
- Use a persistent shared cache so tickets survive restarts
- Queue consumers: reschedule on cache miss instead of failing
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
- Failed to get suite_access_token: %s
- Failed to get auth_corp_info: %s
- Failed to get access_token: %s
- Failed to get jssdk ticket: %s
- Failed to get jssdk agentTicket: %s
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/28d19a54f5dfa750.
Report an issue: GitHub.