{"record":{"id":"3c552d2f8aba881d","repo":"pentaho/pentaho-kettle","slug":"gpg-exceptionillegalthreadstateexception","errorCode":null,"errorMessage":"GPG.ExceptionillegalThreadStateException","messagePattern":"GPG\\.ExceptionillegalThreadStateException","errorType":"exception","errorClass":"KettleException","httpStatus":null,"severity":"warning","filePath":"engine/src/main/java/org/pentaho/di/job/entries/pgpencryptfiles/GPG.java","lineNumber":223,"sourceCode":"      }\n    }\n\n    try {\n      p.waitFor();\n\n      psr_stdout.join();\n      psr_stderr.join();\n    } catch ( InterruptedException i ) {\n      throw new KettleException( BaseMessages.getString( PKG, \"GPG.ExceptionWait\" ), i );\n    }\n\n    try {\n      if ( p.exitValue() != 0 ) {\n        throw new KettleException( BaseMessages.getString( PKG, \"GPG.Exception.ExistStatus\", psr_stderr\n          .getString() ) );\n      }\n    } catch ( IllegalThreadStateException itse ) {\n      throw new KettleException( BaseMessages.getString( PKG, \"GPG.ExceptionillegalThreadStateException\" ), itse );\n    } finally {\n      p.destroy();\n    }\n\n    retval = psr_stdout.getString();\n\n    return retval;\n\n  }\n\n  /**\n   * Decrypt a file\n   *\n   * @param cryptedFilename\n   *          crypted filename\n   * @param passPhrase\n   *          passphrase for the personal private key to sign with\n   * @param decryptedFilename","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/job/entries/pgpencryptfiles/GPG.java#L205-L241","documentation":"KettleException with key GPG.ExceptionillegalThreadStateException is thrown by GPG.execGnuPG when p.exitValue() is called while the gpg process has not yet terminated (IllegalThreadStateException). This indicates a race in the code: waitFor() succeeded but a caller path reaches exitValue() before the process is truly done. The process is destroyed in the finally block.","triggerScenarios":"Any of decryptFile, encryptFile, signAndEncryptFile, signFile, verifySignature, verifyDetachedSignature when p.exitValue() is queried before the gpg process has exited.","commonSituations":"Rare JVM/threading timing issue after waitFor(); abnormal gpg process states; heavily loaded systems where thread joins return while process bookkeeping lags.","solutions":["Replace p.exitValue() with the int returned by p.waitFor(), which cannot throw IllegalThreadStateException.","Ensure psr_stdout.join()/psr_stderr.join() completed before checking exit status.","If it recurs, capture the gpg command line and run it manually to check for hangs.","Upgrade the Pentaho Kettle engine version where this race may be fixed."],"exampleFix":"// before\np.waitFor();\n...\nif ( p.exitValue() != 0 ) {\n// after\nint exitCode = p.waitFor();\n...\nif ( exitCode != 0 ) {","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  gpg.signFile(file, userId, signedFile, false);\n} catch (KettleException ke) {\n  if (ke.getCause() instanceof IllegalThreadStateException) {\n    // process state race — retry once\n  }\n}","preventionTips":["Use a Kettle version where waitFor()'s return value is used for the exit code","Avoid extreme system load during GPG operations","Log the gpg command line when this recurs"],"tags":["gpg","process","thread-state","race-condition"],"backgroundTag":"internal-invariant-violation","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"}