dromara/Sa-Token · error · RuntimeException
当前线程非JavaWeb环境
Error message
当前线程非JavaWeb环境
What it means
Thrown by SoMap.getRequestSoMap() when Spring's RequestContextHolder.getRequestAttributes() returns null on the current thread — meaning the call is not executing inside an HTTP request bound by Spring MVC. The static helper relies on SpringMVC's thread-local request context, which only exists on request-handler threads.
Source
Thrown at sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java:578
// try {
// return JSON.toJSONString(this, true);
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
// ============================= web辅助 =============================
/**
* 返回当前request请求的的所有参数
* @return
*/
public static SoMap getRequestSoMap() {
// 大善人SpringMVC提供的封装
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new RuntimeException("当前线程非JavaWeb环境");
}
// 当前request
HttpServletRequest request = servletRequestAttributes.getRequest();
if (request.getAttribute("currentSoMap") == null || request.getAttribute("currentSoMap") instanceof SoMap == false ) {
initRequestSoMap(request);
}
return (SoMap)request.getAttribute("currentSoMap");
}
/** 初始化当前request的 SoMap */
private static void initRequestSoMap(HttpServletRequest request) {
SoMap soMap = new SoMap();
Map<String, String[]> parameterMap = request.getParameterMap(); // 获取所有参数
for (String key : parameterMap.keySet()) {
try {
String[] values = parameterMap.get(key); // 获得values
if(values.length == 1) {
soMap.set(key, values[0]);View on GitHub (pinned to ac2c7f6e94)
Solutions
- Move the getRequestSoMap() call onto the HTTP request thread (inside the controller) and pass the SoMap/values as arguments to the async code.
- For tests: RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
- If truly needed on other threads, capture RequestContextHolder.setRequestAttributes(attributes, true) (inheritable) before spawning — but explicit parameter passing is safer.
- Check that spring-boot-starter-web (not just webflux) is on the classpath so ServletRequestAttributes is what gets bound.
Example fix
// before
@Async
public void process() {
SoMap soMap = SoMap.getRequestSoMap(); // throws
}
// after
public void handle(HttpServletRequest request) {
SoMap soMap = SoMap.getRequestSoMap();
process(soMap.getString("code"));
}
@Async
public void process(String code) { ... } Defensive patterns
Strategy: validation
Validate before calling
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs == null) {
// not on a request thread: pass values explicitly instead of calling getRequestSoMap()
} Type guard
boolean isRequestThread() { return RequestContextHolder.getRequestAttributes() != null; } Prevention
- Never call getRequestSoMap from @Async/@Scheduled/new threads; capture the SoMap on the handler thread and pass it down.
- In tests, set MockHttpServletRequest via RequestContextHolder.setRequestAttributes before invoking controller code.
When it happens
Trigger: Calling getRequestSoMap() (or any code path that uses it) from: a @Scheduled method, an @Async method without context propagation, a newly spawned thread / CompletableFuture lambda, a Filter running before RequestContextFilter is registered, or a unit test that never set up MockHttpServletRequest via RequestContextHolder.setRequestAttributes(...).
Common situations: Refactoring a controller helper into a background job; running demos under Servlet 3 async dispatch where the context is not propagated; plain junit tests calling controller logic directly.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/45b3fac837787fd4.
Report an issue: GitHub.