{"record":{"id":"94380e66fb167433","repo":"w7corp/easywechat","slug":"failed-to-get-suite-access-token-s","errorCode":null,"errorMessage":"Failed to get suite_access_token: %s","messagePattern":"Failed to get suite_access_token: (.+?)","errorType":"exception","errorClass":"HttpException","httpStatus":null,"severity":"error","filePath":"src/OpenWork/SuiteAccessToken.php","lineNumber":88,"sourceCode":"    {\n        return ['suite_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_suite_token', [\n            'json' => [\n                'suite_id' => $this->suiteId,\n                'suite_secret' => $this->suiteSecret,\n                'suite_ticket' => $this->suiteTicket?->getTicket(),\n            ],\n        ])->toArray(false);\n\n        if (empty($response['suite_access_token'])) {\n            throw new HttpException('Failed to get suite_access_token: '.json_encode(\n                $response,\n                JSON_UNESCAPED_UNICODE\n            ));\n        }\n\n        $this->cache->set(\n            $this->getKey(),\n            $response['suite_access_token'],\n            abs(intval($response['expires_in']) - 100)\n        );\n\n        return $response['suite_access_token'];\n    }\n}\n","sourceCodeStart":70,"sourceCodeEnd":103,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/OpenWork/SuiteAccessToken.php#L70-L103","documentation":"Thrown by SuiteAccessToken::refresh() when the POST to cgi-bin/service/get_suite_token returns a body without suite_access_token. The suite token requires three correct inputs: suite_id, suite_secret, and a valid suite_ticket pushed by WeChat every ~10 minutes to your callback URL. The raw response with errcode is JSON-embedded in the message.","triggerScenarios":"Any OpenWork API call needing suite_access_token (e.g. get_auth_corpa_info, get_corp_token) on cache miss. Fails when suite_id/suite_secret mismatch, or the suite_ticket is missing, stale, or belongs to another suite (errcode 40001, 40013, 61004 'invalid suite_ticket').","commonSituations":"Callback URL for receiving suite_ticket not configured or not reachable (so the ticket never lands in cache); cache driver flushed/short TTL so the ticket expired; using the suite_ticket of one suite with credentials of another; ticket overwritten by multiple suites sharing one cache key prefix.","solutions":["Check embedded errcode in the exception message (61004/61003 = ticket problem, 40001 = secret problem)","Confirm the suite_ticket callback (data type 'suite_ticket') is configured and reachable, and that SuiteTicket::setTicket() is being called from your callback handler","Verify suite_id and suite_secret match the same suite in the open platform console","If tickets are pushed but still failing, ensure cache is shared/persistent across the callback process and the API process"],"exampleFix":"// in your OpenWork callback controller, persist the pushed ticket\n// before\n$server->serve(); // ticket never saved\n// after\n$server->handle('suite_ticket', function (Message $message, SuiteTicket $suiteTicket) {\n    $suiteTicket->setTicket($message->SuiteTicket, $message->info expiresIn);\n    return 'success';\n});\nreturn $server->serve();","handlingStrategy":"retry","validationCode":"try {\n    $suiteTicket->getTicket(); // ticket present?\n} catch (\\EasyWeChat\\Kernel\\Exceptions\\RuntimeException $e) {\n    // ticket not pushed yet - wait for next push\n}","typeGuard":null,"tryCatchPattern":"use EasyWeChat\\Kernel\\Exceptions\\HttpException;\ntry {\n    $token = $openWork->suite_access_token->getToken();\n} catch (HttpException $e) {\n    if (str_contains($e->getMessage(), '61004') || str_contains($e->getMessage(), 'suite_ticket')) {\n        // wait for next ticket push (<=10 min) then retry once\n        retryIn(600, fn () => $openWork->suite_access_token->getToken());\n    }\n    throw $e;\n}","preventionTips":["Persist suite_ticket in shared durable cache (Redis) from the callback handler","Monitor the callback endpoint so pushes never 5xx","Do not run multiple suites on overlapping cache key prefixes"],"tags":["wechat-work","open-work","suite-access-token","suite-ticket","credentials"],"backgroundTag":"access-token-request-failed","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}