{"record":{"id":"4ba413fd21196793","repo":"dromara/Sa-Token","slug":"code-30303","errorCode":"CODE_30303","errorMessage":"token 已超时，无法解析：{jwtToken}","messagePattern":"token 已超时，无法解析：(.+?)","errorType":"exception","errorClass":"SaTokenException","httpStatus":null,"severity":"error","filePath":"sa-token-plugin/sa-token-temp-jwt/src/main/java/cn/dev33/satoken/temp/jwt/SaJwtUtil.java","lineNumber":107,"sourceCode":"\t\t\t\t.verifyWith(key)\n\t\t\t\t.build()\n\t\t\t\t.parseSignedClaims(jwtToken).getPayload();\n    }\n\n    /**\n     * 从一个 jwt-token 解析出载荷, 并取出数据\n     * @param jwtToken JwtToken值 \n     * @param keyt 秘钥\n     * @return 值 \n     */\n    public static Object getValue(String jwtToken, String keyt) {\n    \t// 取出数据 \n    \tClaims claims = parseToken(jwtToken, keyt);\n    \t\n    \t// 验证是否超时 \n    \tLong eff = claims.get(KEY_EFF, Long.class);\n    \tif(eff == null || (eff < System.currentTimeMillis() && eff != NEVER_EXPIRE)) {\n    \t\tthrow new SaTokenException(\"token 已超时，无法解析：\" + jwtToken).setCode(SaTempJwtErrorCode.CODE_30303);\n    \t}\n    \t\n        // 获取数据 \n        return claims.get(KEY_VALUE);\n    }\n\n    /**\n     * 从一个 jwt-token 解析出载荷, 并取出其剩余有效期\n     * @param jwtToken JwtToken值 \n     * @param keyt 秘钥\n     * @return 值 \n     */\n    public static long getTimeout(String jwtToken, String keyt) {\n    \t// 取出数据 \n    \tClaims claims = parseToken(jwtToken, keyt);\n\n    \t// 验证是否超时 \n    \tLong eff = claims.get(KEY_EFF, Long.class);","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-plugin/sa-token-temp-jwt/src/main/java/cn/dev33/satoken/temp/jwt/SaJwtUtil.java#L89-L125","documentation":"Thrown by SaJwtUtil.getValue when parsing a temp JWT whose embedded effective-time claim (KEY_EFF) is null or in the past (and not the NEVER_EXPIRE sentinel). sa-token temp-jwt tokens are stateless — validity is enforced purely by this embedded timestamp, since there is no server-side record to delete.","triggerScenarios":"Calling SaTempTemplateForJwt.getValue(token)/parseToken-based reads on a jwt-temp-token after its effective window elapsed; also fires if the token was created without an eff claim (null).","commonSituations":"A long-running job holds a temp token issued with a short timeout; server clock skew between issuing and validating machines; using jwt temp tokens as if they were revocable session tokens.","solutions":["Re-issue a fresh temp token via SaTempUtil.createToken(value, timeout) when this error occurs","Issue the token with a longer timeout or SaTempUtil.NEVER_EXPIRE if it must outlive the job","Sync clocks (NTP) across issuing and validating nodes if tokens expire suspiciously early"],"exampleFix":"// before\nObject value = SaTempUtil.parseToken(jwtToken, secretKey);\n\n// after — refresh on expiry\nObject value;\ntry {\n    value = SaJwtUtil.getValue(jwtToken, secretKey);\n} catch (SaTokenException e) {\n    if(SaTempJwtErrorCode.CODE_30303 == e.getCode()) {\n        jwtToken = SaTempUtil.createToken(originalValue, 60 * 60 * 24);\n        value = SaJwtUtil.getValue(jwtToken, secretKey);\n    } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"long remain = SaJwtUtil.getTimeout(jwtToken, keyt);\nif(remain <= 0) {\n    // refresh the token before use\n}","typeGuard":null,"tryCatchPattern":"try { SaJwtUtil.getValue(jwtToken, keyt); } catch (SaTokenException e) { if(SaTempJwtErrorCode.CODE_30303 == e.getCode()) { /* re-issue temp token */ } }","preventionTips":["Choose temp-token timeout from the consumer job's expected duration","Never treat jwt temp tokens as revocable; design callers to re-issue on expiry"],"tags":["sa-token","jwt","expiry","stateless"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}