w7corp/easywechat · error · HttpException

code2Session error: %s

Error message

code2Session error: %s

What it means

Utils::codeToSession() calls GET /sns/jscode2session and expects openid in the response; WeChat reports failures with errcode inside an HTTP 200 body (40029 invalid js_code, 40163 code already used, 45011 frequency limit, -1 system busy), so any response without openid becomes HttpException with the full JSON appended. This is the exchange step of mini-program login (wx.login code → openid + session_key).

Source

Thrown at src/MiniApp/Utils.php:28

    {
    }

    /**
     * @throws HttpException
     */
    public function codeToSession(string $code): array
    {
        $response = $this->app->getHttpClient()->request('GET', '/sns/jscode2session', [
            'query' => [
                'appid' => $this->app->getAccount()->getAppId(),
                'secret' => $this->app->getAccount()->getSecret(),
                'js_code' => $code,
                'grant_type' => 'authorization_code',
            ],
        ])->toArray(false);

        if (empty($response['openid'])) {
            throw new HttpException('code2Session error: '.json_encode($response, JSON_UNESCAPED_UNICODE));
        }

        return $response;
    }

    public function decryptSession(string $sessionKey, string $iv, string $ciphertext): array
    {
        return Decryptor::decrypt($sessionKey, $iv, $ciphertext);
    }

    /**
     * @throws HttpException
     */
    public function getPhoneNumber(string $code): array
    {
        $response = $this->app->createClient()->request('POST', '/wxa/business/getuserphonenumber', [
            'json' => [
                'code' => $code,

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Use each js_code exactly once, immediately after wx.login; request a fresh code on failure.
  2. Confirm appid and secret come from the same mini program console entry and are current (re-copy the secret).
  3. Decode the JSON inside the exception message and branch on errcode: 40029/40163 → ask the client for a new code; -1/45011 → retry with backoff.
  4. Cache openid/session_key briefly after a successful exchange so retries do not re-exchange codes.

Example fix

// before: replaying a consumed js_code
$session = $app->getUtils()->codeToSession($sameOldCode);
// after: branch on errcode and request a fresh code when invalid
try {
    $session = $app->getUtils()->codeToSession($code);
} catch (HttpException $e) {
    if (str_contains($e->getMessage(), '40029')) {
        return $this->askClientForNewCode();
    }
    throw $e;
}
Defensive patterns

Strategy: retry

Validate before calling

$code = trim($code);
if ($code === '') { throw new InvalidArgumentException('js_code required'); }
if (!Cache::add('js_code:'.$code, 1, 300)) { throw new RuntimeException('code already consumed'); }

Try / catch

try { $session = $app->getUtils()->codeToSession($code); } catch (\EasyWeChat\Kernel\Exceptions\HttpException $e) { $body = json_decode(substr($e->getMessage(), (int) strpos($e->getMessage(), '{')), true); $err = (int) ($body['errcode'] ?? 0); if (in_array($err, [-1, 45011], true)) { usleep(500000); return $app->getUtils()->codeToSession($code); } // 40029/40163: ask the client for a fresh wx.login code throw new DomainException('wx login rejected: errcode '.$err); }

Prevention

When it happens

Trigger: Exchanging a js_code twice (codes are single-use and short-lived); appid+secret pair mismatched (secret from another app, or reset in the console); calling jscode2session beyond the per-minute quota (45011); appid belonging to a different mini program than the code; empty secret because env vars differ per environment.

Common situations: WeChat dev tools (test appid) against a production secret; page refresh replaying the login with the same code; load tests tripping 45011; secret rotated in the console but not redeployed.

Related errors


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