{"record":{"id":"079c34bb48e6d30d","repo":"w7corp/easywechat","slug":"failed-to-get-access-token-s-079c34","errorCode":null,"errorMessage":"Failed to get access_token: %s","messagePattern":"Failed to get access_token: (.+?)","errorType":"exception","errorClass":"HttpException","httpStatus":null,"severity":"error","filePath":"src/OpenWork/AuthorizerAccessToken.php","lineNumber":99,"sourceCode":"     */\n    public function refresh(): string\n    {\n        if (! isset($this->suiteAccessToken)) {\n            return '';\n        }\n\n        $response = $this->httpClient->request('POST', 'cgi-bin/service/get_corp_token', [\n            'query' => [\n                'suite_access_token' => $this->suiteAccessToken->getToken(),\n            ],\n            'json' => [\n                'auth_corpid' => $this->corpId,\n                'permanent_code' => $this->permanentCodeOrAccessToken,\n            ],\n        ])->toArray(false);\n\n        if (empty($response['access_token'])) {\n            throw new HttpException('Failed to get access_token: '.json_encode($response, JSON_UNESCAPED_UNICODE));\n        }\n\n        $this->cache->set($this->getKey(), $response['access_token'], intval($response['expires_in']));\n\n        return $response['access_token'];\n    }\n}\n","sourceCodeStart":81,"sourceCodeEnd":107,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/OpenWork/AuthorizerAccessToken.php#L81-L107","documentation":"OpenWork\\AuthorizerAccessToken fetches an authorizer corp access_token via cgi-bin/service/get_corp_token (auth_corpid plus permanent_code, authorized by the suite token) and throws HttpException with the embedded JSON when access_token is absent. Every API call made on behalf of the authorizer corp funnels through this, so the exception marks the whole authorizer credential layer as broken.","triggerScenarios":"The authorizer corp removed the suite app so permanent_code is invalid; a wrong or mismatched corp id / permanent code pair was persisted; the suite_access_token carried in the query string expired or was rejected.","commonSituations":"Long-lived installations where the authorizer later uninstalled the app; per-corp rows missing after migration; suite token caches wiped across all nodes at once, making every corp call fail together.","solutions":["Read the embedded errcode/errmsg to distinguish invalid permanent code from invalid suite credential","If the authorization was revoked, flag the authorizer record and require reinstall through the suite flow","Refresh the suite access token, then retry the corp-token fetch once","Keep permanent codes stored per corp and update them on every re-authorization"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (blank($corpId) || blank($permanentCode)) {\n    throw new \\RuntimeException('corpId and permanent_code are required to fetch an authorizer token.');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $token = $authorizerAccessToken->getToken();\n} catch (\\EasyWeChat\\Kernel\\Exceptions\\HttpException $e) {\n    $payload = json_decode(strstr($e->getMessage(), '{') ?: '[]', true) ?: [];\n    if (str_contains((string) ($payload['errmsg'] ?? ''), 'invalid permanent')) {\n        $corpRow->update(['status' => 'reauth_required']);\n    }\n    report($e);\n}","preventionTips":["Cache authorizer access tokens and refresh only on expiry, not per call","Store permanent codes per corp and update them on re-authorization","Distinguish 'revoked authorization' from 'transient credential failure' in error handling"],"tags":["php","easywechat","wechat-work","open-work","access-token","authorization","http-error"],"backgroundTag":"access-token-request-failed","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}