dromara/Sa-Token · error · SaOAuth2Exception
30141
30141
Error message
应用未开放的 grant_type:
What it means
Thrown by SaOAuth2Strategy.grantTypeAuth after clientSecret/scope validation: the grant type is valid and globally enabled, but this client's allowGrantTypes does not include it. Note the code assigned here is 30141 (the 'system not enabled' code) even though the message says the client has not enabled it — a code-reuse quirk in the library. Error code 30141.
Source
Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java:210
}
// 针对 authorization_code 与 password 两种特殊 grant_type,需要判断全局是否开启
SaOAuth2ServerConfig config = SaOAuth2Manager.getServerConfig();
if(grantType.equals(GrantType.authorization_code) && !config.getEnableAuthorizationCode() ) {
throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);
}
if(grantType.equals(GrantType.password) && !config.getEnablePassword() ) {
throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);
}
// 校验 clientSecret 和 scope
ClientIdAndSecretModel clientIdAndSecretModel = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
List<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(req.getParam(SaOAuth2Consts.Param.scope));
SaClientModel clientModel = SaOAuth2Manager.getTemplate().checkClientSecretAndScope(clientIdAndSecretModel.getClientId(), clientIdAndSecretModel.getClientSecret(), scopes);
// 检测应用是否开启此 grantType
if(!clientModel.getAllowGrantTypes().contains(grantType)) {
throw new SaOAuth2Exception("应用未开放的 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30141);
}
// 调用 处理器构建 Access-Token
return grantTypeHandler.getAccessToken(req, clientIdAndSecretModel.getClientId(), scopes);
};
// ------------------ 凭证创建 ------------------
/**
* 创建一个 code value
*/
public SaOAuth2CreateCodeValueFunction createCodeValue = (clientId, loginId, scopes) -> {
return SaFoxUtil.getRandomString(60);
};
/**
* 创建一个 AccessToken valueView on GitHub (pinned to ac2c7f6e94)
Solutions
- Add the needed grant type to the client's allowGrantTypes registration
- Alternatively switch the caller to a grant type the client already allows
- When catching, do not rely solely on code 30141 to distinguish system-level vs client-level disabling — inspect the message or the client config
Example fix
// before
new SaClientModel()
.setClientId("1001")
.setAllowGrantTypes(Collections.singletonList("authorization_code"));
// after
new SaClientModel()
.setClientId("1001")
.setAllowGrantTypes(Arrays.asList("authorization_code", "password")); Defensive patterns
Strategy: validation
Validate before calling
SaClientModel cm = oauth2Template.checkClientModel(clientId);
if(!cm.getAllowGrantTypes().contains(grantType)) {
throw new IllegalStateException("client " + clientId + " not registered for grant " + grantType);
} Try / catch
catch(SaOAuth2Exception e) {
// note: library reuses 30141 here; match on message too
if("30141".equals(e.getCode()) && e.getMessage().contains("应用未开放")) return 403 "client not permitted";
} Prevention
- Do not key error handling on the numeric code alone for this case — the message distinguishes client vs system
- Review allowGrantTypes whenever a new integration starts using an existing client_id
When it happens
Trigger: POST /oauth2/token with grant_type=password for a client registered with allowGrantTypes=[authorization_code]; any grant request whose type is missing from the client's registered list.
Common situations: Client provisioned only for web-server flow but a mobile or legacy integration uses password grant; client registry entries defined per environment with divergent allowGrantTypes.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/4607c668e1df34e4.
Report an issue: GitHub.