{"record":{"id":"ce92d96b498ccddf","repo":"dromara/Sa-Token","slug":"30105","errorCode":"30105","errorMessage":"无效 client_id: ","messagePattern":"无效 client_id: ","errorType":"exception","errorClass":"SaOAuth2ClientModelException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/loader/SaOAuth2DataLoader.java","lineNumber":54,"sourceCode":"     *\n     * @param clientId 应用id\n     * @return ClientModel\n     */\n    default SaClientModel getClientModel(String clientId) {\n        // 默认从内存配置中读取数据\n        return SaOAuth2Manager.getServerConfig().getClients().get(clientId);\n    }\n\n    /**\n     * 根据 id 获取 Client 信息，不允许为 null\n     *\n     * @param clientId 应用id\n     * @return ClientModel\n     */\n    default SaClientModel getClientModelNotNull(String clientId) {\n        SaClientModel clientModel = getClientModel(clientId);\n        if(clientModel == null) {\n            throw new SaOAuth2ClientModelException(\"无效 client_id: \" + clientId)\n                    .setClientId(clientId)\n                    .setCode(SaOAuth2ErrorCode.CODE_30105);\n        }\n        return clientModel;\n    }\n\n    /**\n     * 根据 ClientId 和 LoginId 获取 openid\n     *\n     * @param clientId 应用id\n     * @param loginId 账号id\n     * @return 此账号在此Client下的openid\n     */\n    default String getOpenid(String clientId, Object loginId) {\n        return SaSecureUtil.md5(SaOAuth2Manager.getServerConfig().getOpenidDigestPrefix() + \"_\" + clientId + \"_\" + loginId);\n    }\n\n    /**","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/data/loader/SaOAuth2DataLoader.java#L36-L72","documentation":"Thrown by SaOAuth2DataLoader.getClientModelNotNull when getClientModel(clientId) returns null — no SaClientModel is registered for that client_id in the server config. Code 30105 marks an invalid client_id; the exception carries the clientId.","triggerScenarios":"Any OAuth2 endpoint (authorize, token) receiving a client_id that is not a key of sa-token.oauth2-server.clients in the configuration, or a custom SaOAuth2DataLoader implementation returning null for that id.","commonSituations":"client_id typo in the client application; the client was registered in a different environment's config; the 'clients' block is missing/malformed (wrong indentation) so no clients load at all; custom data loader forgets to handle a new client.","solutions":["Add (or fix) the client entry under sa-token.oauth2-server.clients with the exact client-id the client sends","Verify the client application's configured client_id matches char-for-char (case-sensitive)","If you implement SaOAuth2DataLoader yourself, make getClientModel return a model for every valid client id"],"exampleFix":"# before\nsa-token:\n  oauth2-server:\n    clients:\n      - client-id: my-web   # client sends \"my-app\" -> 30105\n\n# after\nsa-token:\n  oauth2-server:\n    clients:\n      - client-id: my-app\n        client-secret: xxx\n        ...","handlingStrategy":"validation","validationCode":"SaClientModel cm = saOAuth2DataLoader.getClientModel(clientId);\nif (cm == null) {\n    return badRequest(\"unknown client_id: \" + clientId);\n}","typeGuard":null,"tryCatchPattern":"catch (SaOAuth2ClientModelException e) { if (e.getCode() == SaOAuth2ErrorCode.CODE_30105) { /* 400 invalid_client */ } }","preventionTips":["Keep client ids in shared config/constants used by both the client app and the server config","Validate the clients block loads at startup (non-empty map) to catch YAML indentation errors early"],"tags":["sa-token","oauth2","client-id","configuration"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}