{"record":{"id":"25534f4f61420d08","repo":"pentaho/pentaho-kettle","slug":"system-exit-not-allowed","errorCode":null,"errorMessage":"System exit not allowed","messagePattern":"System exit not allowed","errorType":"exception","errorClass":"SecurityException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/security/ExitInterceptor.java","lineNumber":31,"sourceCode":"\npackage org.pentaho.di.security;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\npublic class ExitInterceptor {\n    private static final AtomicBoolean interceptEnabled = new AtomicBoolean(false);\n\n    public static void enableIntercept() {\n        interceptEnabled.set( true );\n    }\n\n    public static void disableIntercept() {\n        interceptEnabled.set( false );\n    }\n\n    public static void exit( int status ) {\n        if ( interceptEnabled.get() ) {\n            throw new SecurityException( \"System exit not allowed\" );\n        }\n        System.exit( status );\n    }\n}\n","sourceCodeStart":13,"sourceCodeEnd":36,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/security/ExitInterceptor.java#L13-L36","documentation":"ExitInterceptor.exit throws this SecurityException when interception is enabled and code attempts to call System.exit(status). Kettle enables this interceptor in embedded/test contexts to prevent plugins or scripts from killing the whole JVM. If interception is disabled, the call proceeds to the real System.exit.","triggerScenarios":"Any code path (plugin, JavaScript rule, script value) calling System.exit() or Runtime halt-equivalents routed through ExitInterceptor.exit while ExitInterceptor.enableIntercept() is active.","commonSituations":"Running transformations/jobs embedded in an application server or test harness where a plugin calls System.exit on error; executing kettle scripts that terminate the JVM; unit tests exercising failure paths of such plugins.","solutions":["If a controlled shutdown is genuinely needed, call ExitInterceptor.disableIntercept() before exit, then re-enable","Fix the plugin/script to return an error status instead of calling System.exit","In embedded scenarios, catch SecurityException around job/transformation execution and translate it into an application-level error","Only disable interception in contexts where exiting the JVM is safe"],"exampleFix":"// before\nSystem.exit(1);\n// after\ntry {\n  ExitInterceptor.exit(1);\n} catch (SecurityException e) {\n  LOG.error(\"System.exit blocked in embedded context; returning error instead\");\n}","handlingStrategy":"try-catch","validationCode":"if (ExitInterceptor.isInterceptEnabled()) {\n  LOG.warn(\"System.exit calls will be blocked in this context\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  runTransformation();\n} catch (SecurityException e) {\n  if (\"System exit not allowed\".equals(e.getMessage())) {\n    LOG.error(\"Plugin attempted System.exit in embedded JVM\");\n  } else throw e;\n}","preventionTips":["In embedded contexts, always enable ExitInterceptor and catch SecurityException at the execution boundary","Refactor plugins/scripts to return errors rather than calling System.exit","Only disable interception where terminating the JVM is acceptable","Run kettle scripts in isolated processes if they may call System.exit"],"tags":["security","system-exit","embedding","jvm"],"backgroundTag":"unsupported-operation","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"}