justauth/JustAuth · error · AuthException

object.get("error") + ":" + object.get("error_description")

Error message

object.get("error") + ":" + object.get("error_description")

What it means

AuthQqRequest.getOpenId throws AuthException built from 'error:error_description' when QQ's /oauth2.0/me endpoint (response stripped of the JSONP callback wrapper) contains an error key. This endpoint exchanges an access token for openid/unionid.

Source

Thrown at src/main/java/me/zhyd/oauth/request/AuthQqRequest.java:91

    /**
     * 获取QQ用户的OpenId,支持自定义是否启用查询unionid的功能,如果启用查询unionid的功能,
     * 那就需要开发者先通过邮件申请unionid功能,参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D}
     *
     * @param authToken 通过{@link AuthQqRequest#getAccessToken(AuthCallback)}获取到的{@code authToken}
     * @return openId
     */
    private String getOpenId(AuthToken authToken) {
        String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me")
            .queryParam("access_token", authToken.getAccessToken())
            .queryParam("unionid", config.isUnionId() ? 1 : 0)
            .build()).getBody();
        String removePrefix = response.replace("callback(", "");
        String removeSuffix = removePrefix.replace(");", "");
        String openId = removeSuffix.trim();
        JSONObject object = JSONObject.parseObject(openId);
        if (object.containsKey("error")) {
            throw new AuthException(object.get("error") + ":" + object.get("error_description"));
        }
        authToken.setOpenId(object.getString("openid"));
        if (object.containsKey("unionid")) {
            authToken.setUnionId(object.getString("unionid"));
        }
        return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId();
    }

    /**
     * 返回获取userInfo的url
     *
     * @param authToken 用户授权token
     * @return 返回获取userInfo的url
     */
    @Override
    protected String userInfoUrl(AuthToken authToken) {
        return UrlBuilder.fromBaseUrl(source.userInfo())
            .queryParam("access_token", authToken.getAccessToken())

View on GitHub (pinned to 694bbf1b01)

Solutions

  1. Refresh the access token before calling getOpenId; QQ access tokens expire per expires_in returned at token time
  2. Confirm config.getClientId() is the same QQ Connect appid under which the token was issued
  3. Catch AuthException and inspect the error/error_description pair (e.g. 100016) to decide refresh vs re-login
  4. Persist the openid/unionid after first retrieval to avoid repeated me-endpoint calls

Example fix

null
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
    String openId = qqRequest.getOpenId(token); // via getUserInfo path
} catch (AuthException e) {
    if (String.valueOf(e.getMessage()).contains("100016")) {
        token = qqRequest.refresh(token).getData();
        // retry once
    }
}

Prevention

When it happens

Trigger: getUserInfo or getOpenId running with an access token QQ has expired or invalidated (error 100016 invalid access token), or a malformed token passed in.

Common situations: Stored QQ token reused after expiry, token from a different QQ Connect app (appid mismatch with oauth_consumer_key), or the JSONP-stripping replace failing because QQ changed the callback format.

Related errors


AI-assisted analysis of justauth/JustAuth@694bbf1b01 (2026-08-14). Data as JSON: /api/errors/0d03cd912f6634a2. Report an issue: GitHub.