pentaho/pentaho-kettle · error · SessionRecoveryRetryException
Unable to invoke repository operation after reconnect
Error message
Unable to invoke repository operation after reconnect
What it means
RepositorySessionTimeoutHandler.reconnectAndRetry throws SessionRecoveryRetryException('Unable to invoke repository operation after reconnect') when the reflective re-invocation fails with IllegalAccessException or IllegalArgumentException — i.e. reflection could not legally call the method (inaccessible method, mismatched argument types/receiver), rather than the method itself failing. This indicates a programming or proxy mismatch problem, not a server problem.
Solutions
- Ensure the intercepted repository methods are public and the argument types exactly match the Method object's declaring class
- Re-obtain the Method object from the current instance's class instead of caching it across reconnects
- Check for plugin classloader conflicts after reconnection
Example fix
// before Method m = Repository.class.getMethod( "save", ... ); // cached, may mismatch instance // after Method m = repository.getClass().getMethod( "save", ... ); // resolve from live instance
Defensive patterns
Strategy: validation
Validate before calling
// verify the method is reflectively invokable before reconnect flows
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 Modifier.isPublic( m.getModifiers() );
} Try / catch
try {
handler.handle( proxy, method, args );
} catch ( SessionRecoveryRetryException e ) {
if ( e.getCause() instanceof IllegalAccessException
|| e.getCause() instanceof IllegalArgumentException ) {
log.error( "Reflection mismatch in timeout handler", e.getCause() ); // bug, not transient
} else { throw e; }
} Prevention
- Keep intercepted repository methods public and signatures stable across upgrades
- Resolve Method objects from the live instance's class each time, never cache across reconnects
- Beware plugin classloader boundaries when re-invoking after reconnection
When it happens
Trigger: The repository proxy passes arguments that no longer match the method signature, the method is not accessible from the handler's package/class loader, or the receiver instance is of the wrong type after reconnection.
Common situations: Classloader issues with plugins after reconnect; custom Repository implementations whose methods are not public; version upgrade changed a method signature the handler caches.
Related errors
- Failed to retry repository operation after reconnect
- Attempting to create PDI Repository with no Active…
- Failed to create repository for user
- Failed to re-invoke operation after successful login
- Session recovery failed or was canceled
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/87a729565c70a6ee.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/repositories/core/src/main/java/org/pentaho/di/ui/repo/timeout/RepositorySessionTimeoutHandler.java:151
private Object reconnectAndRetry( Method method, Object[] args )
throws SessionRecoveryRetryException {
try {
ConnectionManager.getInstance().reset();
} catch ( Exception ce ) {
// Intentionally ignore cache reset errors - they should not prevent reconnection
}
try {
metaStoreInstance = wrapMetastoreWithTimeoutHandler( MetaStoreConst.getDefaultMetastore(), sessionTimeoutHandler );
} catch ( Exception me ) {
// Intentionally ignore metastore refresh errors - they should not prevent reconnection
}
try {
return method.invoke( this.repository, args );
} catch ( InvocationTargetException retryEx ) {
throw new SessionRecoveryRetryException( "Failed to retry repository operation after reconnect",
retryEx.getCause() );
} catch ( IllegalAccessException | IllegalArgumentException retryEx ) {
throw new SessionRecoveryRetryException( "Unable to invoke repository operation after reconnect", retryEx );
}
}
static class SessionRecoveryRetryException extends KettleException {
private static final long serialVersionUID = 1L;
SessionRecoveryRetryException( String message, Throwable cause ) {
super( message, cause );
}
}
boolean connectedToRepository() {
return repository.isConnected();
}
IRepositoryService wrapRepositoryServiceWithTimeoutHandler( Class<? extends IRepositoryService> clazz )
throws KettleException {
IRepositoryService service = repository.getService( clazz );View on GitHub (pinned to f3058517a1)