alibaba/spring-ai-alibaba · error · RuntimeException

无法修改final字段

Error message

无法修改final字段: <fieldName>

What it means

NacosModelInjector.modifyFinalFieldWithUnsafe fails to modify a final field of an object through reflection plus an Unsafe internal API, and wraps any failure in a RuntimeException naming the field. This usually means the Unsafe access technique is not viable on the current JVM (method renamed/removed, module access denied) or the target object/field is mismatched.

Solutions

  1. Read the wrapped cause to see whether it is NoSuchMethodException (JDK removed the API) or IllegalAccessException (module restrictions).
  2. Run the JVM with --add-opens flags for the relevant modules if encapsulation is the blocker.
  3. Prefer designing the target class with a mutable field or constructor injection instead of final-field mutation.
  4. Pin/adjust the JDK version to one supported by NacosModelInjector's Unsafe approach.

Example fix

// before: JVM flags omitted
java -jar app.jar
// after: open required modules so Unsafe reflection works
java --add-opens java.base/jdk.internal.misc=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED -jar app.jar
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect Unsafe availability on the current JVM before attempting injection
try {
    Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
    unsafeClass.getMethod("staticFieldOffset", Field.class);
} catch (ClassNotFoundException | NoSuchMethodException e) {
    throw new IllegalStateException("Unsafe final-field injection unsupported on this JVM: " + e.getMessage());
}

Try / catch

try {
    injector.modifyFinalField(target, field, value);
} catch (RuntimeException e) {
    logger.error("Final field {} not modifiable: {}", field.getName(), e.getCause(), e);
    throw new IllegalStateException("Configuration injection failed; fix the field design instead", e);
}

Prevention

When it happens

Trigger: modifyFinalField called on a field of an object where the Unsafe class lookup, staticFieldOffset invocation, or putObject invocation fails — e.g. on JDK 9+ with strong encapsulation blocking sun.misc.Unsafe internals, or a null targetObject.

Common situations: Upgrading to a newer JDK that removes or restricts the internal Unsafe API (the code reflects on method names like staticFieldOffset); running with a Security Manager or JPMS settings blocking reflective access; injecting into record-like/final classes.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/1605b69dcf7c5782. Report an issue: GitHub.

Appendix: source

Thrown at spring-boot-starters/spring-ai-alibaba-starter-config-nacos/src/main/java/com/alibaba/cloud/ai/agent/nacos/NacosModelInjector.java:107

	/**
	 * 使用Unsafe修改final字段(适用于Java 12+)
	 */
	private static void modifyFinalFieldWithUnsafe(Field field, Object targetObject, Object newValue) throws Exception {
		try {
			Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
			Field unsafeInstanceField = unsafeClass.getDeclaredField("theUnsafe");
			unsafeInstanceField.setAccessible(true);
			Object unsafeInstance = unsafeInstanceField.get(null);

			Method putObjectMethod = unsafeClass.getMethod("putObject", Object.class, long.class, Object.class);
			Method staticFieldOffsetMethod = unsafeClass.getMethod("staticFieldOffset", Field.class);

			long offset = (Long) staticFieldOffsetMethod.invoke(unsafeInstance, field);
			putObjectMethod.invoke(unsafeInstance, targetObject, offset, newValue);
		}
		catch (Exception e) {
			throw new RuntimeException("无法修改final字段: " + field.getName(), e);
		}
	}
}

View on GitHub (pinned to f82da0b50f)