{"record":{"id":"7445d6935f2b26b9","repo":"dromara/Sa-Token","slug":"code-30004","errorCode":"CODE_30004","errorMessage":"无效 ticket : {ticket}","messagePattern":"无效 ticket : (.+?)","errorType":"exception","errorClass":"SaSsoException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java","lineNumber":180,"sourceCode":"     * @param cs 要转换的类型\n     * @return 账号id\n     */\n    public <T> T getLoginId(String ticket, Class<T> cs) {\n        return SaFoxUtil.getValueByType(getLoginId(ticket), cs);\n    }\n\n    // 校验\n\n    /**\n     * 校验 Ticket，无效 ticket 会抛出异常\n     *\n     * @param ticket Ticket码\n     * @return /\n     */\n    public TicketModel checkTicket(String ticket) {\n        TicketModel ticketModel = getTicket(ticket);\n        if(ticketModel == null) {\n            throw new SaSsoException(\"无效 ticket : \" + ticket).setCode(SaSsoErrorCode.CODE_30004);\n        }\n        return ticketModel;\n    }\n\n    /**\n     * 校验 Ticket 码，无效 ticket 会抛出异常，如果此ticket是有效的，则立即删除\n     * @param ticket Ticket码\n     * @return 账号id\n     */\n    public TicketModel checkTicketParamAndDelete(String ticket) {\n        return checkTicketParamAndDelete(ticket, SaSsoConsts.CLIENT_WILDCARD);\n    }\n\n    /**\n     * 校验 Ticket，无效 ticket 会抛出异常，如果此ticket是有效的，则立即删除\n     *\n     * @param ticket Ticket码\n     * @param client client 标识","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-sso/src/main/java/cn/dev33/satoken/sso/template/SaSsoServerTemplate.java#L162-L198","documentation":"Thrown by SaSsoServerTemplate.checkTicket when getTicket(ticket) returns null — the ticket string does not exist in the server's storage. SSO tickets are short-lived, one-time-use random strings created at /sso/auth; a lookup miss means the ticket was never issued, has been consumed, or has expired.","triggerScenarios":"Client calls the server's check-ticket endpoint with a ticket that was already deleted (tickets are removed on first validation via checkTicketParamAndDelete), has passed its timeout, or is a malformed/guessed value.","commonSituations":"User double-refreshes the client callback URL (first refresh consumed the ticket); the login page sat open longer than the ticket timeout; Redis data was flushed or the server restarted with in-memory storage; clock/store mismatch in a clustered server where the ticket was created on a different node without shared storage.","solutions":["Redirect the user back to the SSO login/auth flow instead of showing a raw error — the ticket is gone and must be reissued","Increase ticket validity (sa-token.sso.ticket-timeout) if users idle on the callback page","Ensure all sso-server nodes share the same Redis/storage so tickets created on one node are visible on another"],"exampleFix":"// before\nTicketModel tm = ssoServerTemplate.checkTicketParamAndDelete(ticket);\n\n// after — degrade to re-login instead of a 500\ntry {\n    TicketModel tm = ssoServerTemplate.checkTicketParamAndDelete(ticket);\n} catch (SaSsoException e) {\n    if(SaSsoErrorCode.CODE_30004 == e.getCode()) {\n        return SaHolder.getResponse().redirect(ssoServerTemplate.buildServerAuthUrl());\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"if(ssoServerTemplate.getTicket(ticket) == null) {\n    // ticket unknown: redirect to auth flow instead of validating\n}","typeGuard":null,"tryCatchPattern":"try { ssoServerTemplate.checkTicket(ticket); } catch (SaSsoException e) { if(SaSsoErrorCode.CODE_30004 == e.getCode()) { /* redirect to /sso/auth to reissue */ } else throw e; }","preventionTips":["Make ticket validation idempotent-safe: tolerate double callbacks by redirecting to re-auth","Use shared Redis storage across server nodes and size ticket-timeout realistically"],"tags":["sa-token","sso","ticket","expiry"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}