{"record":{"id":"4607c668e1df34e4","repo":"dromara/Sa-Token","slug":"30141-4607c6","errorCode":"30141","errorMessage":"应用未开放的 grant_type: ","messagePattern":"应用未开放的 grant_type: ","errorType":"exception","errorClass":"SaOAuth2Exception","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java","lineNumber":210,"sourceCode":"\t\t}\n\n\t\t// 针对 authorization_code 与 password 两种特殊 grant_type，需要判断全局是否开启\n\t\tSaOAuth2ServerConfig config = SaOAuth2Manager.getServerConfig();\n\t\tif(grantType.equals(GrantType.authorization_code) && !config.getEnableAuthorizationCode() ) {\n\t\t\tthrow new SaOAuth2Exception(\"系统未开放的 grant_type: \" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);\n\t\t}\n\t\tif(grantType.equals(GrantType.password) && !config.getEnablePassword() ) {\n\t\t\tthrow new SaOAuth2Exception(\"系统未开放的 grant_type: \" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);\n\t\t}\n\n\t\t// 校验 clientSecret 和 scope\n\t\tClientIdAndSecretModel clientIdAndSecretModel = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);\n\t\tList<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(req.getParam(SaOAuth2Consts.Param.scope));\n\t\tSaClientModel clientModel = SaOAuth2Manager.getTemplate().checkClientSecretAndScope(clientIdAndSecretModel.getClientId(), clientIdAndSecretModel.getClientSecret(), scopes);\n\n\t\t// 检测应用是否开启此 grantType\n\t\tif(!clientModel.getAllowGrantTypes().contains(grantType)) {\n\t\t\tthrow new SaOAuth2Exception(\"应用未开放的 grant_type: \" + grantType).setCode(SaOAuth2ErrorCode.CODE_30141);\n\t\t}\n\n\t\t// 调用 处理器构建 Access-Token\n\t\treturn grantTypeHandler.getAccessToken(req, clientIdAndSecretModel.getClientId(), scopes);\n\t};\n\n\n\t// ------------------ 凭证创建 ------------------\n\n\t/**\n\t * 创建一个 code value\n\t */\n\tpublic SaOAuth2CreateCodeValueFunction createCodeValue = (clientId, loginId, scopes) -> {\n\t\treturn SaFoxUtil.getRandomString(60);\n\t};\n\n\t/**\n\t * 创建一个 AccessToken value","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java#L192-L228","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nnew SaClientModel()\n    .setClientId(\"1001\")\n    .setAllowGrantTypes(Collections.singletonList(\"authorization_code\"));\n\n// after\nnew SaClientModel()\n    .setClientId(\"1001\")\n    .setAllowGrantTypes(Arrays.asList(\"authorization_code\", \"password\"));","handlingStrategy":"validation","validationCode":"SaClientModel cm = oauth2Template.checkClientModel(clientId);\nif(!cm.getAllowGrantTypes().contains(grantType)) {\n    throw new IllegalStateException(\"client \" + clientId + \" not registered for grant \" + grantType);\n}","typeGuard":null,"tryCatchPattern":"catch(SaOAuth2Exception e) {\n    // note: library reuses 30141 here; match on message too\n    if(\"30141\".equals(e.getCode()) && e.getMessage().contains(\"应用未开放\")) return 403 \"client not permitted\";\n}","preventionTips":["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"],"tags":["oauth2","grant-type","client-registration","sa-token"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}