{"record":{"id":"15a4e56da56aec4d","repo":"dromara/Sa-Token","slug":"code-30005","errorCode":"CODE_30005","errorMessage":"{result.getMsg()}","messagePattern":"\\{result\\.getMsg\\(\\)\\}","errorType":"exception","errorClass":"SaSsoException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoClientProcessor.java","lineNumber":357,"sourceCode":"\t\t\tif(SaFoxUtil.isNotEmpty(cfg.getCurrSsoLogoutCall())) {\n\t\t\t\tssoLogoutCall = cfg.getCurrSsoLogoutCall();\n\t\t\t}\n\t\t\t// 如果提供了当前 uri，则根据此值来计算：\n\t\t\telse if(SaFoxUtil.isNotEmpty(currUri)) {\n\t\t\t\tssoLogoutCall = SaHolder.getRequest().getUrl().replace(currUri, apiName.ssoLogoutCall);\n\t\t\t}\n\t\t\t// 否则视为不注册单点注销回调地址\n\t\t\telse {\n\t\t\t}\n\t\t}\n\n\t\t// 发起请求\n\t\tSaSsoMessage message = ssoClientTemplate.buildCheckTicketMessage(ticket, ssoLogoutCall);\n\t\tSaResult result = ssoClientTemplate.pushMessageAsSaResult(message);\n\n\t\t// 如果 sso-server 响应的状态码非200，代表业务失败，将回应的 msg 字段作为异常抛出\n\t\tif(result.getCode() == null || result.getCode() != SaResult.CODE_SUCCESS) {\n\t\t\tthrow new SaSsoException(result.getMsg()).setCode(SaSsoErrorCode.CODE_30005);\n\t\t}\n\n\t\t// 构建返回结果\n\t\tSaCheckTicketResult ctr = new SaCheckTicketResult();\n\t\tctr.loginId = result.get(paramName.loginId);\n\t\tctr.tokenValue = result.get(paramName.tokenValue, String.class);\n\t\tctr.deviceId = result.get(paramName.deviceId, String.class);\n\t\tctr.remainTokenTimeout = result.get(paramName.remainTokenTimeout, Long.class);\n\t\tctr.remainSessionTimeout = result.get(paramName.remainSessionTimeout, Long.class);\n\t\tctr.result = result;\n\n\t\t// 转换 loginId 和 centerId\n\t\tctr.centerId = ctr.loginId;\n\t\tctr.loginId = ssoClientTemplate.strategy.convertCenterIdToLoginId.run(ctr.centerId);\n\n\t\treturn ctr;\n\t}\n","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/processor/SaSsoClientProcessor.java#L339-L375","documentation":"Thrown during ticket validation (SSO mode 2/3): the client sent a check-ticket message to the sso-server and the response's code was not 200. The exception carries the server's msg, so the actual failure reason (invalid ticket, bad sign, missing client config) is whatever the server replied.","triggerScenarios":"Client callback /sso/login?ticket=... invokes ssoClientTemplate.checkTicket(ticket); the pushMessageAsSaResult call for buildCheckTicketMessage returns a SaResult whose code != 200 (or code is null because the response was not a valid SaResult at all).","commonSituations":"Ticket already consumed or expired on the server; secret-key mismatch causing sign verification failure on the server; server URL misconfigured so the client hit a 404 page; a gateway/filter intercepting the server-to-server call.","solutions":["Inspect the exact msg string — it mirrors the sso-server's error (invalid ticket vs sign error vs client not configured)","Ensure the ticket is validated only once and immediately after redirect (tickets are one-time use in checkTicketParamAndDelete)","Verify secret-key, server-url and client identifier match between the two sides"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    SaCheckTicketResult r = ssoClientTemplate.checkTicket(ticket);\n} catch (SaSsoException e) {\n    if(SaSsoErrorCode.CODE_30005 == e.getCode()) {\n        // rethrow result.getMsg(); typically redirect back to /sso/auth to reissue ticket\n    }\n}","preventionTips":["Never re-validate the same ticket; consume it once on callback","Check the propagated msg — it is the server's real failure reason"],"tags":["sa-token","sso","ticket","remote-call"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}