w7corp/easywechat · critical · HttpException

Failed to get access_token: %s

Error message

Failed to get access_token: %s

What it means

OfficialAccount\AccessToken::getAccessToken() is the classic token path: it GETs cgi-bin/token with grant_type=client_credential and the appid/secret, then throws HttpException with the raw JSON when no access_token comes back. WeChat rejected the client credentials or the calling IP. Every authenticated API call funnels through this token, so this failure blocks the whole account.

Source

Thrown at src/OfficialAccount/AccessToken.php:123

    /**
     * @throws HttpException
     */
    public function getAccessToken(): string
    {
        $response = $this->httpClient->request(
            'GET',
            'cgi-bin/token',
            [
                'query' => [
                    'grant_type' => 'client_credential',
                    'appid' => $this->appId,
                    'secret' => $this->secret,
                ],
            ]
        )->toArray(false);

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

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

        return $response['access_token'];
    }
}

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. json_decode the embedded JSON and act on errcode (40125/41004 → secret, 40013 → appid, 40164 → IP whitelist)
  2. Whitelist the production egress IP in MP console → 基本配置 → IP名单
  3. Fix the env/config values and clear the app config cache, then retry
  4. Confirm you are not mixing the appid of one account with the secret of another

Example fix

// before: env key typo → empty secret sent to WeChat
$config = ['app_id' => env('WECHAT_APP_ID'), 'secret' => env('WECHAT_SECRET')];

// after
$config = ['app_id' => env('WECHAT_APP_ID'), 'secret' => env('WECHAT_OFFICIAL_ACCOUNT_SECRET')];
Defensive patterns

Strategy: try-catch

Validate before calling

if (blank($app->getConfig()->get('app_id')) || blank($app->getConfig()->get('secret'))) {
    throw new \RuntimeException('OfficialAccount app_id/secret must be set before token requests.');
}

Try / catch

try {
    $token = $accessToken->getAccessToken();
} catch (\EasyWeChat\Kernel\Exceptions\HttpException $e) {
    $payload = json_decode(strstr($e->getMessage(), '{') ?: '[]', true) ?: [];
    // 40164 → whitelist IP; 40125/41004 → secret; 40013 → appid. Map each to a specific alert.
    report($e);
}

Prevention

When it happens

Trigger: Wrong appid or secret in config; server egress IP missing from the MP console IP whitelist (errcode 40164); secret rotated in the console but not in env; daily token quota/IP changes after infrastructure moves.

Common situations: Works locally but fails in production (different egress IP); credential drift between environments; containerized deployments with NAT'd IPs nobody whitelisted; env var typo (WECHAT_SECRET vs WECHAT_OFFICIAL_ACCOUNT_SECRET).

Related errors


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