dromara/Sa-Token · error · SaOAuth2Exception

30126

30126

Error message

无效 grant_type: 

What it means

Thrown by SaOAuth2Strategy.grantTypeAuth: the grant_type parameter does not match any registered grant-type handler. Built-in handlers cover authorization_code and password (plus client_credentials on its own endpoint); anything unknown fails this lookup. Error code 30126.

Source

Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/strategy/SaOAuth2Strategy.java:191

		SaManager.getLog().info("自定义 GRANT_TYPE [{}] (处理器: {})", handler.getHandlerGrantType(), handler.getClass().getCanonicalName());
	}

	/**
	 * 移除一个 grant_type 处理器
	 */
	public void removeGrantTypeHandler(String scope) {
		grantTypeHandlerMap.remove(scope);
	}

	/**
	 * 根据 grantType 构造一个 AccessTokenModel
	 */
	public SaOAuth2GrantTypeAuthFunction grantTypeAuth = (req) -> {
		// 先校验提供的 grant_type 是否有效
		String grantType = req.getParamNotNull(SaOAuth2Consts.Param.grant_type);
		SaOAuth2GrantTypeHandlerInterface grantTypeHandler = grantTypeHandlerMap.get(grantType);
		if(grantTypeHandler == null) {
			throw new SaOAuth2Exception("无效 grant_type: " + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);
		}

		// 针对 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)) {

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Use a supported grant_type: authorization_code or password on /oauth2/token (client_credentials goes to /oauth2/client_token)
  2. For custom grant types, register the handler before use: SaOAuth2Strategy.instance.addGrantTypeHandler("my_grant", handler)
  3. Check for typos/whitespace in the grant_type parameter

Example fix

// before
// custom grant type used but never registered
POST /oauth2/token  grant_type=my_grant

// after
SaOAuth2Strategy.instance.addGrantTypeHandler("my_grant", new MyGrantTypeHandler());
POST /oauth2/token  grant_type=my_grant
Defensive patterns

Strategy: validation

Validate before calling

Set<String> supported = new HashSet<>(Arrays.asList("authorization_code", "password"));
// plus any handler you registered via addGrantTypeHandler
if(!supported.contains(grantType)) {
    throw new IllegalArgumentException("unsupported grant_type: " + grantType);
}

Try / catch

catch(SaOAuth2Exception e) { if("30126".equals(e.getCode())) return badRequest("invalid grant_type: " + e.getMessage()); }

Prevention

When it happens

Trigger: POST /oauth2/token with grant_type values such as 'refresh_token' when no such handler is registered, 'client_credentials' (handled on /oauth2/client_token, not here), typos, or a custom grant type whose handler was never added via addGrantTypeHandler.

Common situations: Developer expects standard OAuth2 refresh_token support out of the box; custom grant type defined but handler registration missing or keyed under a different string; version upgrade where a custom handler was registered on a different strategy instance.

Related errors


AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14). Data as JSON: /api/errors/aafa52f45426aa28. Report an issue: GitHub.