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

  1. Read e.getCause() from the thrown RuntimeException to find the real failure (InstantiationException vs IllegalAccessException vs IllegalArgumentException)
  2. Ensure the target class is public, concrete, and has a public/protected no-arg constructor (add @NoArgsConstructor alongside @Builder)
  3. Match map value types to field types or convert values before calling (e.g. String.valueOf for numeric map values)
  4. 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

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.