mybatis/mybatis-3 · error · ReflectionException

Could not get property '{}' from {}. Cause: {}

Error message

Could not get property '{}' from {}.  Cause: {}

What it means

BeanWrapper.getBeanProperty invokes the property's read method via reflection. If the getter itself throws (any non-runtime Throwable, unwrapped via ExceptionUtil.unwrapThrowable), MyBatis wraps it in a ReflectionException naming the property, the owner class, and the cause. The real failure is inside the getter's code — the exception message's Cause is the key.

Source

Thrown at src/main/java/org/apache/ibatis/reflection/wrapper/BeanWrapper.java:197

    } catch (Exception e) {
      throw new ReflectionException("Cannot set value of property '" + name + "' because '" + name
          + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);
    }
    return metaValue;
  }

  private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        return method.invoke(object, NO_ARGUMENTS);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (RuntimeException e) {
      throw e;
    } catch (Throwable t) {
      throw new ReflectionException(
          "Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);
    }
  }

  private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      Invoker method = metaClass.getSetInvoker(prop.getName());
      Object[] params = { value };
      try {
        method.invoke(object, params);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (Throwable t) {
      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass()
          + "' with value '" + value + "' Cause: " + t.toString(), t);
    }
  }

View on GitHub (pinned to 008069adb1)

Solutions

  1. Read the Cause in the exception message and fix the underlying code in the getter (usually an NPE on an unset field)
  2. Make the getter defensive: return null/default when its dependencies are not initialized
  3. Avoid referencing computed/logic-heavy getters in mapper expressions; map stored fields instead
  4. Initialize the fields the getter depends on (constructor or field initializers)

Example fix

// before
public String getFullName() { return firstName.concat(" ").concat(lastName); } // NPE when null

// after
public String getFullName() {
  return Stream.of(firstName, lastName).filter(Objects::nonNull).collect(Collectors.joining(" "));
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  Object v = metaObject.getValue("fullName");
} catch (ReflectionException e) {
  Throwable cause = e.getCause(); // inspect: the getter itself failed
  // fix getter defensively; do not blanket-catch and continue in production paths
  throw new IllegalStateException("Getter failed for fullName: " + cause, cause);
}

Prevention

When it happens

Trigger: A getter that dereferences an uninitialized internal field and throws NPE; a getter performing computation/parse (e.g. getFullName() concatenating nulls, getDuration() dividing by zero); a getter throwing a checked exception; mapping a transient/computed property in a resultMap or #{expr} evaluation.

Common situations: Lazy-loading proxies inside getters failing after session close; getters with business logic that break on edge-case state (null fields, empty strings); computed getters referenced in mapper expressions that assume populated state.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/9bee32b9bba4e523. Report an issue: GitHub.