pinpoint-apm/pinpoint · error · IllegalArgumentException

Cannot add setter to final field. setterMethod: ${setterName

Error message

Cannot add setter to final field. setterMethod: ${setterName}, fieldName: ${fieldName}

What it means

ASMClass.addSetter() refuses to add a setter to a field declared final, because writing it would violate the class's declared invariants. The caller may pass removeFinal=true to have the engine strip the ACC_FINAL flag before weaving; otherwise the transform is rejected with this IllegalArgumentException naming the setter method and field.

Source

Thrown at agent-module/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/ASMClass.java:349

            final ASMFieldNodeAdapter fieldNode = this.classNode.getField(fieldName, null);
            if (fieldNode == null) {
                throw new IllegalArgumentException("Not found field. name=" + fieldName);
            }

            final Type fieldType = setterDetails.getFieldType();
            if (!fieldNode.getJavaType().equals(fieldType)) {
                throw new IllegalArgumentException("Argument type of the setter is different with the field type. setterMethod: " + fieldType + ", fieldType: " + fieldNode.getJavaType());
            }

            if (fieldNode.isStatic()) {
                throw new IllegalArgumentException("Cannot add setter to static fields. setterMethod: " + setterDetails.getSetter().getName() + ", fieldName: " + fieldName);
            }

            final int original = fieldNode.getAccess();
            boolean finalRemoved = false;
            if (fieldNode.isFinal()) {
                if (!removeFinal) {
                    throw new IllegalArgumentException("Cannot add setter to final field. setterMethod: " + setterDetails.getSetter().getName() + ", fieldName: " + fieldName);
                } else {
                    final int removed = original & ~Opcodes.ACC_FINAL;
                    fieldNode.setAccess(removed);
                    finalRemoved = true;
                }
            }

            try {
                boolean modified = finalRemoved;
                modified |= this.classNode.addSetterMethod(setterDetails.getSetter().getName(), fieldNode);
                modified |= this.classNode.addInterface(setterClass.getName());
                if (modified) {
                    setModified(true);
                } else if (logger.isDebugEnabled()) {
                    logger.debug("Skip addSetter, setter already declared. class={}, setter={}", getName(), setterClass.getName());
                }
            } catch (Exception e) {
                if (finalRemoved) {

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Pass removeFinal=true to addSetter if mutating the final field is intentional
  2. Choose a non-final field, or drop the setter approach and use an interceptor on accessing methods
  3. Verify with javap whether the field is final in the exact target library version

Example fix

// before
clazz.addSetter(setterClass, "url"); // url is final
// after
clazz.addSetter(setterClass, "url", true); // removeFinal = true
Defensive patterns

Strategy: validation

Validate before calling

Field f = targetClass.getDeclaredField(fieldName);
if (Modifier.isFinal(f.getModifiers())) {
    // decide: pass removeFinal=true or pick another field
}

Type guard

boolean isMutableField(Class<?> c, String name) {
    try { int m = c.getDeclaredField(name).getModifiers(); return !Modifier.isFinal(m) && !Modifier.isStatic(m); } catch (NoSuchFieldException e) { return false; }
}

Prevention

When it happens

Trigger: Calling the addSetter API against a final field while removeFinal is false (default). fieldNode.isFinal() is true in the target class bytecode.

Common situations: Targeting immutable/value fields or constants accidentally; library versions where a field was made final; plugin authors unaware that removeFinal must be explicitly enabled.

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


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/223d483f939341a1. Report an issue: GitHub.