w7corp/easywechat · critical · HttpException

Failed to get component_access_token: %s

Error message

Failed to get component_access_token: %s

What it means

ComponentAccessToken fetches the component_access_token by POSTing component appid, secret and the current component_verify_ticket to cgi-bin/component/api_component_token, then caches it (expires_in minus 100 seconds). HttpException with the embedded JSON is thrown when WeChat returns no component_access_token — one of the three ingredients was rejected. Every open-platform API call depends on this token, so the whole component goes down when it fails.

Source

Thrown at src/OpenPlatform/ComponentAccessToken.php:86

    /**
     * @throws HttpException
     */
    public function refresh(): string
    {
        $response = $this->httpClient->request(
            'POST',
            'cgi-bin/component/api_component_token',
            [
                'json' => [
                    'component_appid' => $this->appId,
                    'component_appsecret' => $this->secret,
                    'component_verify_ticket' => $this->verifyTicket->getTicket(),
                ],
            ]
        )->toArray(false);

        if (empty($response['component_access_token'])) {
            throw new HttpException('Failed to get component_access_token: '.json_encode(
                $response,
                JSON_UNESCAPED_UNICODE
            ));
        }

        $this->cache->set(
            $this->getKey(),
            $response['component_access_token'],
            abs(intval($response['expires_in']) - 100)
        );

        return $response['component_access_token'];
    }
}

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Decode the embedded errcode/errmsg — invalid-secret and invalid-ticket codes point to different fixes
  2. Fix the component secret in config if it was rotated, then redeploy/clear config cache
  3. Ensure verify-ticket pushes land on the shared cache so the freshest ticket is always used
  4. Retry after the next ticket push if the cache was stale

Example fix

// before: per-instance file cache → consumers read a stale component_verify_ticket → 40001 cycle
$openPlatform->setCache(new \Symfony\Component\Cache\Adapter\FilesystemAdapter());

// after: shared Redis for both the push receiver and every worker
$openPlatform->setCache(new \Symfony\Component\Cache\Adapter\RedisAdapter($redis, 'easywechat'));
Defensive patterns

Strategy: try-catch

Validate before calling

if (blank($config['component_appid'] ?? null) || blank($config['component_secret'] ?? null)) {
    throw new \RuntimeException('OpenPlatform component appid/secret must be set.');
}

Try / catch

try {
    $token = $componentAccessToken->getToken();
} catch (\EasyWeChat\Kernel\Exceptions\HttpException $e) {
    $payload = json_decode(strstr($e->getMessage(), '{') ?: '[]', true) ?: [];
    $errcode = $payload['errcode'] ?? null;
    // 40001 → stale verify ticket (check push receiver + shared cache); 40125-class → secret
    report($e);
}

Prevention

When it happens

Trigger: component_verify_ticket stale (cache holds an old push while WeChat rotated it); wrong component_appid/component_appsecret; the ticket cache wiped mid-flight so the ticket used is empty/old; the component app not approved or frozen.

Common situations: Secret rotated in the console but not in env; file/local caches not shared so consumers read an old ticket; deploys switching Redis or changing cache prefixes; new component apps before their first ticket push arrives.

Related errors


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