dromara/Sa-Token · error · SaOAuth2ClientModelException
30105
30105
Error message
无效 client_id:
What it means
Thrown by SaOAuth2Template.checkClientModel: no SaClientModel exists for the given client_id (the data loader returned null). This is the primary 'unknown client' error for every OAuth2 endpoint that validates a client. It is thrown as SaOAuth2ClientModelException carrying the clientId. Error code 30105.
Source
Thrown at sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/template/SaOAuth2Template.java:61
/**
* 获取 ClientModel,根据 clientId
*
* @param clientId /
* @return /
*/
public SaClientModel getClientModel(String clientId) {
return SaOAuth2Manager.getDataLoader().getClientModel(clientId);
}
/**
* 校验 clientId 信息并返回 ClientModel,如果找不到对应 Client 信息则抛出异常
* @param clientId /
* @return /
*/
public SaClientModel checkClientModel(String clientId) {
SaClientModel clientModel = getClientModel(clientId);
if(clientModel == null) {
throw new SaOAuth2ClientModelException("无效 client_id: " + clientId)
.setClientId(clientId)
.setCode(SaOAuth2ErrorCode.CODE_30105);
}
return clientModel;
}
/**
* 校验:clientId 与 clientSecret 是否正确,正确返回 SaClientModel,不正确抛出异常
* @param clientId 应用id
* @param clientSecret 秘钥
* @return SaClientModel对象
*/
public SaClientModel checkClientSecret(String clientId, String clientSecret) {
SaClientModel cm = checkClientModel(clientId);
if(cm.clientSecret == null || ! cm.clientSecret.equals(clientSecret)) {
throw new SaOAuth2ClientModelException("无效 client_secret: " + clientSecret)
.setClientId(clientId)
.setCode(SaOAuth2ErrorCode.CODE_30115);View on GitHub (pinned to ac2c7f6e94)
Solutions
- Verify the client_id exists in the active registration source (SaOAuth2DataLoader / configured client list / redis)
- Confirm environment alignment: the service the client calls is the one where the client is registered
- Implement/verify SaOAuth2DataLoader.getClientModel returns a SaClientModel for that exact id
Example fix
// before
@Override
public SaClientModel getClientModel(String clientId) {
return null; // never registered
}
// after
@Override
public SaClientModel getClientModel(String clientId) {
return clients.get(clientId); // pre-loaded map: "1001" -> SaClientModel
} Defensive patterns
Strategy: try-catch
Validate before calling
if(SaOAuth2Manager.getDataLoader().getClientModel(clientId) == null) {
throw new IllegalArgumentException("unknown client_id: " + clientId);
} Try / catch
catch(SaOAuth2ClientModelException e) {
if("30105".equals(e.getCode())) return 400 "unknown client_id"; // e.getClientId() available
} Prevention
- Seed client registrations in a startup runner and fail fast if a required client is missing
- Use environment-specific client registries checked into the repo to avoid drift
When it happens
Trigger: Any authorize/token request with an unregistered client_id; client_id correct in one environment but the service points at another data loader/redis DB; client registration written to a store the oauth2-server does not read.
Common situations: Test vs production client registries differ; SaClientModel list configured in application.yml but a custom data loader overrides it and returns null; typo in client_id; registration lost after cache/redis flush.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/8a503375dd134720.
Report an issue: GitHub.