{"record":{"id":"c84526683b51a2b4","repo":"w7corp/easywechat","slug":"failed-to-get-provider-access-token-s","errorCode":null,"errorMessage":"Failed to get provider_access_token: %s","messagePattern":"Failed to get provider_access_token: (.+?)","errorType":"exception","errorClass":"HttpException","httpStatus":null,"severity":"error","filePath":"src/OpenWork/ProviderAccessToken.php","lineNumber":82,"sourceCode":"    public function toQuery(): array\n    {\n        return ['provider_access_token' => $this->getToken()];\n    }\n\n    /**\n     * @throws HttpException\n     */\n    public function refresh(): string\n    {\n        $response = $this->httpClient->request('POST', 'cgi-bin/service/get_provider_token', [\n            'json' => [\n                'corpid' => $this->corpId,\n                'provider_secret' => $this->providerSecret,\n            ],\n        ])->toArray(false);\n\n        if (empty($response['provider_access_token'])) {\n            throw new HttpException('Failed to get provider_access_token: '.\\json_encode(\n                $response,\n                JSON_UNESCAPED_UNICODE\n            ));\n        }\n\n        $this->cache->set($this->getKey(), $response['provider_access_token'], intval($response['expires_in']));\n\n        return $response['provider_access_token'];\n    }\n}\n","sourceCodeStart":64,"sourceCodeEnd":93,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/OpenWork/ProviderAccessToken.php#L64-L93","documentation":"Thrown by ProviderAccessToken::refresh() when the POST to cgi-bin/service/get_provider_token succeeds at HTTP level but the response body contains no provider_access_token key. The full WeChat response (with errcode/errmsg) is JSON-embedded in the exception message, so the real reason is the errcode, not the token itself. This is the service-provider (ISV) token for WeChat Work open platform APIs.","triggerScenarios":"Calling any OpenWork API that needs provider_access_token (e.g. getting auth corp info, corp token) after a cache miss. Fails when: provider_secret is wrong/revoked, corpid of the service provider is wrong, the secret belongs to a different suite/app, or the calling IP is not in the provider's IP allowlist (errcode 40001/40013/60020).","commonSituations":"Copied provider_secret from the wrong panel (corp app secret instead of provider secret); rotated secret but stale value in .env; server IP changed so allowlist blocks it; confusions between corpid of the ISV vs the authed corp.","solutions":["Read the embedded JSON in the exception message and look up the errcode (40001 invalid secret, 60020 IP not in allowlist, 40013 invalid corpid)","Verify corpid + provider_secret in the WeChat Work service-provider admin console (login as the provider, not a member corp)","Add the outbound server IP to the provider IP allowlist and retry","Clear the token cache key after fixing credentials so refresh() actually re-requests"],"exampleFix":"// before (wrong credentials)\n$config = ['corp_id' => $memberCorpId, 'provider_secret' => $corpAppSecret];\n// after (ISV provider credentials)\n$config = [\n    'corp_id' => $providerCorpId,          // the service provider's corpid\n    'provider_secret' => $providerSecret,  // from provider admin console\n    'secret' => $suiteSecret,\n    'token' => ..., 'aes_key' => ...,\n];","handlingStrategy":"try-catch","validationCode":"$candidate = [$corpId, $providerSecret];\nif (in_array(null, $candidate, true) || $providerSecret === '') {\n    throw new \\InvalidArgumentException('corp_id/provider_secret missing');\n}","typeGuard":null,"tryCatchPattern":"use EasyWeChat\\Kernel\\Exceptions\\HttpException;\ntry {\n    $token = $openWork->provider_access_token->getToken();\n} catch (HttpException $e) {\n    $body = json_decode(substr($e->getMessage(), strlen('Failed to get provider_access_token: ')), true);\n    logger()->warning('provider token failed', ['errcode' => $body['errcode'] ?? null]);\n    // 40001/60020 -> credentials/allowlist: do NOT retry;  -> alert\n}","preventionTips":["Store provider credentials in one env-driven config validated at boot","Keep the outbound IP allowlist updated when infrastructure changes","Cache tokens per the library default so refresh only happens on expiry"],"tags":["wechat-work","open-work","provider-access-token","credentials","http-exception"],"backgroundTag":"access-token-request-failed","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}