{"record":{"id":"2bde1149a40c23ea","repo":"pentaho/pentaho-kettle","slug":"no-job-started-for-current-thread","errorCode":null,"errorMessage":"No job started for current Thread","messagePattern":"No job started for current Thread","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"plugins/pur/core/src/main/java/com/pentaho/di/purge/PurgeUtilityLogger.java","lineNumber":102,"sourceCode":"    getLogger().debug( s );\n  }\n\n  public void warn( String s ) {\n    setCodeLine();\n    getLogger().debug( s );\n  }\n\n  @Override\n  public void error( Exception e ) {\n    setCodeLine();\n    getLogger().error( throwableToString( e ) );\n\n  }\n\n  private PurgeUtilityLog getPurgeUtilityLog() {\n    PurgeUtilityLog currentLog = purgeUtilityLog.get();\n    if ( currentLog == null ) {\n      throw new IllegalStateException( \"No job started for current Thread\" );\n    }\n    return currentLog;\n  }\n\n  private Logger getLogger() {\n    return (Logger) getPurgeUtilityLog().getLogger();\n  }\n\n  public boolean hasLogger() {\n    return ( purgeUtilityLog.get() == null ) ? false : true;\n  }\n\n  @Override\n  public void debug( Object arg0 ) {\n    setCodeLine();\n    getLogger().debug( arg0 );\n  }\n","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/pur/core/src/main/java/com/pentaho/di/purge/PurgeUtilityLogger.java#L84-L120","documentation":"PurgeUtilityLogger stores per-thread PurgeUtilityLog state in a ThreadLocal. getPurgeUtilityLog() throws IllegalStateException \"No job started for current Thread\" when startJob() was never called on this thread (or its state was cleared by endJob), so log access is attempted without a started purge job. It's a state-machine error: logging calls are only valid between startJob and endJob on the same thread.","triggerScenarios":"Calling getLogger()/setCurrentFilePath()/endJob() (all of which call getPurgeUtilityLog) on a thread where startJob() was not invoked, after endJob() already cleared the ThreadLocal, or when work was handed to a different thread (executor/child thread) that never started the job.","commonSituations":"Purge utility logging invoked from an async task or worker thread different from the one that called startJob; double-calling endJob(); exception path skipping startJob() but later code still logging; reusing the logger across unrelated threads.","solutions":["Call PurgeUtilityLogger.startJob(...) before any getLogger()/setCurrentFilePath()/endJob() usage on that thread.","Ensure startJob and endJob are paired in a try/finally on the same thread.","If work runs on worker threads, start a job (or propagate a shared log) inside each thread rather than relying on the initiating thread's ThreadLocal.","Guard logging calls so they are skipped when no job is active."],"exampleFix":"// before\nPurgeUtilityLogger.endJob(); // throws if no job on this thread\n// after\nPurgeUtilityLogger.startJob( purgeJob );\ntry {\n  // ... purge work & logging ...\n} finally {\n  PurgeUtilityLogger.endJob();\n}","handlingStrategy":"validation","validationCode":"if ( !jobStartedOnThisThread ) { PurgeUtilityLogger.startJob( job ); }","typeGuard":"boolean logAvailable() { try { /* invoke getPurgeUtilityLog */ return true; } catch ( IllegalStateException e ) { return false; } }","tryCatchPattern":"try { PurgeUtilityLogger.endJob(); } catch ( IllegalStateException e ) { /* no job on this thread: either startJob first or skip */ }","preventionTips":["Always pair startJob/endJob in try/finally on the same thread.","Never access purge logging from a thread that did not start the job.","Avoid double endJob() calls."],"tags":["threading","state-machine","threadlocal","purge"],"backgroundTag":"invalid-state-transition","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"}