{"record":{"id":"d8ff4f2e8eee2a61","repo":"pentaho/pentaho-kettle","slug":"command-execution-was-interrupted","errorCode":null,"errorMessage":"Command execution was interrupted","messagePattern":"Command execution was interrupted","errorType":"exception","errorClass":"SshConnectionException","httpStatus":null,"severity":"warning","filePath":"engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSshConnection.java","lineNumber":470,"sourceCode":"          if ( ex == null ) {\n            // Wait a bit more for exit status to be set\n            Thread.sleep( 100 );\n            ex = ch.getExitStatus();\n          }\n          exit = ex == null ? -1 : ex;\n        } else {\n          // Timeout occurred\n          throw new SshTimeoutException( \"Command execution timed out after \" + timeoutMs + \"ms\" );\n        }\n      }\n      String outStr = stdout.toString( StandardCharsets.UTF_8 );\n      String errStr = stderr.toString( StandardCharsets.UTF_8 );\n      return new ExecResult( outStr, errStr, outStr + errStr, exit, exit != 0 );\n    } catch ( SshTimeoutException e ) {\n      throw e; // Re-throw timeout exceptions as-is\n    } catch ( InterruptedException e ) {\n      Thread.currentThread().interrupt(); // Restore interrupted status\n      throw new SshConnectionException( \"Command execution was interrupted\", e );\n    } catch ( Exception e ) {\n      throw new SshConnectionException( \"Failed to execute command: \" + command, e );\n    }\n  }\n\n  @Override\n  public ExecResult exec( String command ) throws SshConnectionException {\n    return exec( command, config.getCommandTimeoutMillis() );\n  }\n\n  @Override\n  public SftpSession openSftp() throws SshConnectionException {\n    try {\n      var factory = SftpClientFactory.instance();\n      var sftp = factory.createSftpClient( session );\n      return new MinaSftpSession( sftp );\n    } catch ( Exception e ) {\n      throw new SftpException( \"Failed to open SFTP session\", e );","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSshConnection.java#L452-L488","documentation":"exec catches InterruptedException while sleeping/polling the channel exit status and rethrows it as SshConnectionException('Command execution was interrupted'), after restoring the thread's interrupt flag. It means the executing thread was interrupted (e.g. transformation cancelled/stopped) rather than any SSH-level problem.","triggerScenarios":"A calling thread is interrupted during exec's wait loop (Thread.sleep polling) — typically when a Pentaho transformation/job is stopped, or an executor shutdown cancels the task.","commonSituations":"User hits Stop in Spoon while a remote command runs; thread pool shutdown during app redeploy; timeouts in the orchestration layer cancelling workers.","solutions":["Treat it as a cancellation signal: stop the workflow rather than retrying automatically","Check who interrupted the thread (transformation stop, executor shutdown)","If you must kill the remote process, close the SSH connection so the channel is torn down","Avoid swallowing InterruptedException in caller code; propagate or re-interrupt","Design long remote jobs to be resumable so cancellation is safe"],"exampleFix":"// before\ntry { result = conn.exec(cmd, timeout); } catch ( Exception e ) { e.printStackTrace(); } // ignores interruption\n// after\ntry { result = conn.exec(cmd, timeout); }\ncatch ( SshConnectionException e ) {\n  if ( Thread.currentThread().isInterrupted() ) { cleanupRemote( conn ); return null; } // cancelled\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// check before starting long work\nif ( Thread.currentThread().isInterrupted() ) { throw new CancellationException( \"Already interrupted; skip SSH exec\" ); }","typeGuard":null,"tryCatchPattern":"try { result = conn.exec( cmd, timeoutMs ); }\ncatch ( SshConnectionException e ) {\n  if ( \"Command execution was interrupted\".equals( e.getMessage() ) ) {\n    return; // treat as cancellation, do not retry\n  }\n  throw e;\n} finally {\n  if ( Thread.currentThread().isInterrupted() ) { /* propagate cancellation upstream */ }\n}","preventionTips":["Never swallow InterruptedException in surrounding code","Design remote jobs to be resumable so cancellation is harmless","Close the SSH connection on cancel to stop the remote channel","Distinguish cancellation from real failures in your error handling"],"tags":["ssh","interruption","concurrency"],"backgroundTag":"thread-interrupted","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}