MyCATApache/Mycat-Server · error · ObjectAccessException
Invalid final field .
Error message
Invalid final field ${declaringClass}.${fieldName} What it means
validateFieldAccess rejects writing to or reading serializable fields that are declared final when the runtime JVM is not Java 1.5, since final fields cannot reliably be modified via reflection after Java 5. It throws ObjectAccessException 'Invalid final field <declaringClass>.<fieldName>'.
Solutions
- Remove the final modifier from the field (or make it non-final for deserialization paths)
- Use a constructor-based or setter-based population path instead of field reflection
- Add a readResolve/readObject customization in the target class so final fields are set at construction
- If you truly run Java 1.5, verify JVMInfo detection is correct (JVMInfo.is15)
Example fix
// before private final int port = 8080; // blocks reflection write // after private int port = 8080;
Defensive patterns
Strategy: validation
Validate before calling
for (Field f : obj.getClass().getDeclaredFields()) {
if (Modifier.isFinal(f.getModifiers())) {
throw new IllegalStateException("final field not writable via reflection: " + f.getName());
}
} Type guard
boolean nonFinal = (f.getModifiers() & java.lang.reflect.Modifier.FINAL) == 0;
Try / catch
try {
provider.visitSerializableFields(obj, visitor);
} catch (ObjectAccessException e) {
if (e.getMessage().startsWith("Invalid final field")) {
// construct the object via constructor instead
}
} Prevention
- Don't declare deserialized fields final
- Use constructors or readResolve for immutable fields
- Audit target classes with a reflection-based test before config-time
- Verify JVM version expectations match JVMInfo detection
When it happens
Trigger: visitSerializableFields or writeField is called on a field whose modifiers include final, and JVMInfo.is15() is false (JVM is not Java 1.5), so instead of setAccessible the exception is thrown.
Common situations: Deserializing into immutable-style classes with final fields; a field was made final in a class refactor; running on modern JDK where the 1.5 workaround no longer applies.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot construct
- Cannot construct as it does not have a no-args constructor
- Cannot create by JDK serialization
- Constructor for threw an exception
- Could not get field .
AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11).
Data as JSON: /api/errors/dac0baba10ee9d8e.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/io/mycat/config/util/ReflectionProvider.java:182
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
return in.readObject();
} catch (IOException e) {
throw new ObjectAccessException("Cannot create " + type.getName() + " by JDK serialization", e);
} catch (ClassNotFoundException e) {
throw new ObjectAccessException("Cannot find class " + e.getMessage());
}
}
private boolean fieldModifiersSupported(Field field) {
return !(Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()));
}
private void validateFieldAccess(Field field) {
if (Modifier.isFinal(field.getModifiers())) {
if (JVMInfo.is15()) {
field.setAccessible(true);
} else {
throw new ObjectAccessException("Invalid final field " + field.getDeclaringClass().getName() + "."
+ field.getName());
}
}
}
private Object readResolve() {
serializedDataCache = Collections.synchronizedMap(new HashMap<Class<?>, byte[]>());
fieldDictionary = new FieldDictionary();
return this;
}
}View on GitHub (pinned to 65f8d8beb7)