pentaho/pentaho-kettle · error · LoginSuccessReinvokeException

Unable to invoke operation after successful login

Error message

Unable to invoke operation after successful login

What it means

SessionTimeoutHandler.handleLoginSuccess throws LoginSuccessReinvokeException('Unable to invoke operation after successful login') when the reflective re-invocation after login fails with IllegalAccessException or IllegalArgumentException — the method could not be legally invoked at all (accessibility or argument-type mismatch), independent of any server failure. Like its sibling in RepositorySessionTimeoutHandler, this points to a proxy/signature mismatch bug.

Solutions

  1. Make the intercepted methods public and invoke via their declaring class
  2. Re-resolve the Method object from the live instance's class rather than caching it
  3. Verify proxy arguments are not null or of the wrong type before re-invocation

Example fix

// before
method.invoke( objectToHandle, args ); // IllegalArgumentException if receiver type mismatched
// after
if ( !method.getDeclaringClass().isInstance( objectToHandle ) ) {
  method = objectToHandle.getClass().getMethod( method.getName(), method.getParameterTypes() );
}
method.invoke( objectToHandle, args );
Defensive patterns

Strategy: type-guard

Validate before calling

// same invokability check as reflection mismatches
boolean isInvokable( Object receiver, Method m, Object[] args ) {
  return Modifier.isPublic( m.getModifiers() )
      && m.getDeclaringClass().isInstance( receiver )
      && argsMatch( m.getParameterTypes(), args );
}

Type guard

boolean isInvokable( Object receiver, Method m, Object[] args ) {
  if ( !m.getDeclaringClass().isInstance( receiver ) ) return false;
  Class<?>[] pt = m.getParameterTypes();
  if ( args.length != pt.length ) return false;
  for ( int i = 0; i < pt.length; i++ ) {
    if ( args[i] != null && !pt[i].isInstance( args[i] ) ) return false;
  }
  return true;
}

Try / catch

try {
  handler.performLoginAndReinvoke( objectToHandle, method, args );
} catch ( LoginSuccessReinvokeException e ) {
  if ( e.getCause() instanceof IllegalAccessException
       || e.getCause() instanceof IllegalArgumentException ) {
    log.error( "Reflection invocation bug in timeout handler", e.getCause() );
  } else { throw e; }
}

Prevention

When it happens

Trigger: Method is not accessible from the handler, argument types do not match the Method's declaring class, or the receiver object is not an instance of that class after re-login.

Common situations: Custom repository implementations with non-public methods; classloader/plugin version mismatch after reconnect; cached Method from a different class version.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/f531b85828b06026. Report an issue: GitHub.

Appendix: source

Thrown at plugins/repositories/core/src/main/java/org/pentaho/di/ui/repo/timeout/SessionTimeoutHandler.java:169

      }
    }
    return null;
  }

  /**
   * Called after the user successfully logs in. Initializes repository providers and
   * re-invokes the original method.
   */
  private Object handleLoginSuccess( Object objectToHandle, Method method, Object[] args )
      throws LoginSuccessReinvokeException {
    initializeRepositoryProvidersAfterReconnection( getSpoon() );
    try {
      return method.invoke( objectToHandle, args );
    } catch ( InvocationTargetException ex ) {
      throw new LoginSuccessReinvokeException( "Failed to re-invoke operation after successful login",
          ex.getCause() );
    } catch ( IllegalAccessException | IllegalArgumentException ex ) {
      throw new LoginSuccessReinvokeException( "Unable to invoke operation after successful login", ex );
    }
  }

  static class LoginSuccessReinvokeException extends KettleException {
    private static final long serialVersionUID = 1L;

    LoginSuccessReinvokeException( String message, Throwable cause ) {
      super( message, cause );
    }
  }

  /**
   * Wrapper that distinguishes "reinvocation happened and returned a value (possibly null)"
   * from "no reinvocation was performed".  Using a plain {@code Object} return would conflate
   * a legitimate {@code null} return value with "nothing happened".
   */
  static class ReinvokeResult {
    private final Object value;

View on GitHub (pinned to f3058517a1)