dromara/Sa-Token · error · SaTokenContextException

10002

10002

Error message

SaTokenContext 上下文尚未初始化

What it means

SaTokenContextForThreadLocalStaff keeps the current request's model box in a ThreadLocal. getModelBox() throws code 10002 when the ThreadLocal is empty for the calling thread, i.e. setContext() was never called on this thread. Unlike the CODE_10001 errors (no integration at all), this one means the integration exists but the current thread is outside any bound request context.

Source

Thrown at sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextForThreadLocalStaff.java:73

		modelBoxThreadLocal.remove();
	}

	/**
	 * 获取当前线程的 [ Box 存储器 ]
	 * @return /
	 */
	public static SaTokenContextModelBox getModelBoxOrNull() {
		return modelBoxThreadLocal.get();
	}
	
	/**
	 * 获取当前线程的 [ Box 存储器 ], 如果为空则抛出异常
	 * @return /
	 */
	public static SaTokenContextModelBox getModelBox() {
		SaTokenContextModelBox box = modelBoxThreadLocal.get();
		if(box ==  null) {
			throw new SaTokenContextException("SaTokenContext 上下文尚未初始化").setCode(SaErrorCode.CODE_10002);
		}
		return box;
	}

	/**
	 * 在当前线程的 SaRequest 包装对象
	 * 
	 * @return /
	 */
	public static SaRequest getRequest() {
		return getModelBox().getRequest();
	}

	/**
	 * 在当前线程的 SaResponse 包装对象
	 * 
	 * @return /
	 */

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Move the sa-token-dependent logic back into the request thread (pass loginId as a method argument into the async code instead of calling StpUtil there)
  2. If the async code must use sa-token, capture what you need (loginId, token) before leaving the request thread
  3. In tests or carefully controlled non-web code, set a mock context for the thread before calling SaHolder/StpUtil and clear it after

Example fix

// before
CompletableFuture.runAsync(() -> {
    Object id = StpUtil.getLoginId(); // CODE_10002: no context on pool thread
});

// after: capture in the request thread
Object id = StpUtil.getLoginId();
CompletableFuture.runAsync(() -> {
    process(id);
});
Defensive patterns

Strategy: try-catch

Validate before calling

// check for an active context before using SaHolder on arbitrary threads
if (SaTokenContextForThreadLocalStaff.getModelBoxOrNull() == null) {
    // not inside a request: capture loginId before going async instead
}

Try / catch

try {
    Object id = StpUtil.getLoginId();
} catch (SaTokenContextException e) {
    if (SaErrorCode.CODE_10002 == e.getCode()) {
        // thread has no bound context: fall back to a passed-in principal
    }
}

Prevention

When it happens

Trigger: Calling SaHolder.getRequest()/getStorage() or StpUtil APIs from @Scheduled jobs, @Async methods, CompletableFuture.supplyAsync, newly spawned threads, or message consumers — threads on which the web filter never ran setContext().

Common situations: Background token cleanup or push jobs that reuse service methods containing StpUtil calls; async processing detached from the HTTP request; unit tests calling StpUtil without a mock context; timer threads in a servlet app.

Related errors


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