dromara/Sa-Token · error · SaTokenException

CODE_30303

CODE_30303

Error message

token 已超时,无法解析:{jwtToken}

What it means

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.

Source

Thrown at sa-token-plugin/sa-token-temp-jwt/src/main/java/cn/dev33/satoken/temp/jwt/SaJwtUtil.java:107

				.verifyWith(key)
				.build()
				.parseSignedClaims(jwtToken).getPayload();
    }

    /**
     * 从一个 jwt-token 解析出载荷, 并取出数据
     * @param jwtToken JwtToken值 
     * @param keyt 秘钥
     * @return 值 
     */
    public static Object getValue(String jwtToken, String keyt) {
    	// 取出数据 
    	Claims claims = parseToken(jwtToken, keyt);
    	
    	// 验证是否超时 
    	Long eff = claims.get(KEY_EFF, Long.class);
    	if(eff == null || (eff < System.currentTimeMillis() && eff != NEVER_EXPIRE)) {
    		throw new SaTokenException("token 已超时,无法解析:" + jwtToken).setCode(SaTempJwtErrorCode.CODE_30303);
    	}
    	
        // 获取数据 
        return claims.get(KEY_VALUE);
    }

    /**
     * 从一个 jwt-token 解析出载荷, 并取出其剩余有效期
     * @param jwtToken JwtToken值 
     * @param keyt 秘钥
     * @return 值 
     */
    public static long getTimeout(String jwtToken, String keyt) {
    	// 取出数据 
    	Claims claims = parseToken(jwtToken, keyt);

    	// 验证是否超时 
    	Long eff = claims.get(KEY_EFF, Long.class);

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Re-issue a fresh temp token via SaTempUtil.createToken(value, timeout) when this error occurs
  2. Issue the token with a longer timeout or SaTempUtil.NEVER_EXPIRE if it must outlive the job
  3. Sync clocks (NTP) across issuing and validating nodes if tokens expire suspiciously early

Example fix

// before
Object value = SaTempUtil.parseToken(jwtToken, secretKey);

// after — refresh on expiry
Object value;
try {
    value = SaJwtUtil.getValue(jwtToken, secretKey);
} catch (SaTokenException e) {
    if(SaTempJwtErrorCode.CODE_30303 == e.getCode()) {
        jwtToken = SaTempUtil.createToken(originalValue, 60 * 60 * 24);
        value = SaJwtUtil.getValue(jwtToken, secretKey);
    } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

long remain = SaJwtUtil.getTimeout(jwtToken, keyt);
if(remain <= 0) {
    // refresh the token before use
}

Try / catch

try { SaJwtUtil.getValue(jwtToken, keyt); } catch (SaTokenException e) { if(SaTempJwtErrorCode.CODE_30303 == e.getCode()) { /* re-issue temp token */ } }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14). Data as JSON: /api/errors/4ba413fd21196793. Report an issue: GitHub.