dromara/Sa-Token · error · RuntimeException
转换失败: {e.getMessage()}
Error message
转换失败: {e.getMessage()} What it means
SaFoxUtil has a reflection-based helper (used by paths like search/session data -> object mapping) that instantiates the target class via getDeclaredConstructor().newInstance() and copies map entries into declared fields. Any failure during instantiation or field.set (security manager, non-instantiable class, wrong field type, inaccessible constructor) is wrapped in a RuntimeException with message '转换失败: <cause>'. The original exception is preserved as the cause, so its message is the real diagnosis.
Source
Thrown at sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java:405
public static <T> T mapToObject(Map<String, Object> map, Class<T> clazz) {
if(map == null) {
return null;
}
if(clazz == Map.class) {
return (T) map;
}
try {
T obj = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
String fieldName = field.getName();
if (map.containsKey(fieldName)) {
field.setAccessible(true);
field.set(obj, map.get(fieldName));
}
}
return obj;
} catch (Exception e) {
throw new RuntimeException("转换失败: " + e.getMessage(), e);
}
}
/**
* 在url上拼接上kv参数并返回
* @param url url
* @param paramStr 参数, 例如 id=1001
* @return 拼接后的url字符串
*/
public static String joinParam(String url, String paramStr) {
// 如果参数为空, 直接返回
if(paramStr == null || paramStr.length() == 0) {
return url;
}
if(url == null) {
url = "";
}View on GitHub (pinned to ac2c7f6e94)
Solutions
- Read e.getCause() from the thrown RuntimeException to find the real failure (InstantiationException vs IllegalAccessException vs IllegalArgumentException)
- Ensure the target class is public, concrete, and has a public/protected no-arg constructor (add @NoArgsConstructor alongside @Builder)
- Match map value types to field types or convert values before calling (e.g. String.valueOf for numeric map values)
- Prefer manual mapping or a dedicated mapper (Jackson/Gson) for complex objects instead of this reflection helper
Example fix
// before
@Data @Builder
public class UserInfo { ... } // no no-arg ctor -> 转换失败: InstantiationException
// after
@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class UserInfo { ... } Defensive patterns
Strategy: try-catch
Validate before calling
if (clazz.isInterface()
|| Modifier.isAbstract(clazz.getModifiers())) {
throw new IllegalArgumentException("target must be concrete");
}
try { clazz.getDeclaredConstructor(); }
catch (NoSuchMethodException e) { /* add no-arg ctor */ } Try / catch
try {
T obj = SaFoxUtil.mapToModel(map, clazz);
} catch (RuntimeException e) {
Throwable root = e.getCause();
if (root instanceof InstantiationException) { /* missing no-arg ctor */ }
else if (root instanceof IllegalAccessException) { /* access/module issue */ }
else if (root instanceof IllegalArgumentException) { /* type mismatch on a field */ }
} Prevention
- Give mapped models a public no-arg constructor (mind Lombok @Builder)
- Match map value types to field types before converting
- For complex models use a real mapper (Jackson) instead of reflection helpers
When it happens
Trigger: Passing a class with no no-arg constructor, a non-public class, an abstract class/interface, or a map value whose type does not match the target field (e.g. Integer into a String field without a converter).
Common situations: Using sa-token session-object APIs where the entity lost its no-arg constructor after adding Lombok @Builder without @NoArgsConstructor; mapping JSON-parsed maps (numbers as Integer/Long) into typed models; JDK 17+ module access restrictions blocking reflection.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/129cc697ae646214.
Report an issue: GitHub.