w7corp/easywechat · error · HttpException
getPhoneNumber error: %s
Error message
getPhoneNumber error: %s
What it means
Thrown by MiniApp\Utils::getPhoneNumber() when POST /wxa/business/getuserphonenumber returns a non-zero errcode. The raw WeChat response is JSON-encoded into the exception message, so the exact errcode/errmsg is always embedded. It means WeChat accepted the HTTP request but refused to release the phone number for the one-time code you supplied.
Source
Thrown at src/MiniApp/Utils.php:51
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,
],
])->toArray(false);
if (isset($response['errcode']) && $response['errcode'] !== 0) {
throw new HttpException('getPhoneNumber error: '.json_encode($response, JSON_UNESCAPED_UNICODE));
}
if (empty($response['phone_info'])) {
throw new HttpException('getPhoneNumber error: '.json_encode($response, JSON_UNESCAPED_UNICODE));
}
return $response;
}
}
View on GitHub (pinned to f0cf0a8b83)
Solutions
- json_decode the tail of the exception message and branch on errcode/errmsg (40029 = invalid code, 45011 = rate limited, permission-class codes point to account eligibility)
- Make the mini program request a brand-new code for every attempt and send it to the backend exactly once; never reuse a code across requests
- Confirm the mini program is enterprise-certified and the phone-number capability is enabled (and paid where required) in the MP console
- Verify the code was issued for the same appid the backend uses (no direct-vs-component mixups)
Example fix
// before: reusing a code captured earlier (single-use, short TTL)
$code = $session->get('phone_code');
$phone = $utils->getPhoneNumber($code);
// after: always consume the code from this request, exactly once
$code = (string) $request->input('code');
if ($code === '') {
abort(422, 'phone code required');
}
$phone = $utils->getPhoneNumber($code); Defensive patterns
Strategy: try-catch
Validate before calling
if ($code === '' || strlen($code) > 64) {
throw new \InvalidArgumentException('A fresh open-type="getPhoneNumber" code is required.');
} Try / catch
use EasyWeChat\Kernel\Exceptions\HttpException;
try {
$phone = $utils->getPhoneNumber($code);
} catch (HttpException $e) {
$payload = json_decode(strstr($e->getMessage(), '{') ?: '[]', true) ?: [];
if (($payload['errcode'] ?? null) === 40029) {
// code invalid/used: ask the mini program for a new one; do NOT retry the same code
}
report($e);
} Prevention
- Treat phone codes as single-use and short-lived: request a new one for every attempt
- Pass the code straight from the request payload; never store it in session or replay it from logs
- Keep the mini program certified and the phone-number capability enabled in the MP console
- Log the embedded errcode, not just the exception message
When it happens
Trigger: Calling $utils->getPhoneNumber($code) with a code that is expired, already consumed (codes are single-use), truncated, or issued for a different appid; also when the mini program account is not eligible for the phone-number API (individual subject, not certified, or the paid phone-number service not enabled).
Common situations: Frontend caches or replays the code from the open-type="getPhoneNumber" button across retries/debugging; a developer copies a logged request and re-runs it manually; the mini program is a personal (个人) subject or lacks certification; the 2023+ paid phone-number billing is not activated in the MP console.
Related errors
- Failed to get authorization_info: %s
- Failed to get authorizer_access_token: %s
- No secret configured.
- token or aes_key cannot be empty.
- Failed to get stable access_token: %s
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/b8fdde1796b20197.
Report an issue: GitHub.