{"record":{"id":"d5ec0f7ca788fb3b","repo":"pentaho/pentaho-kettle","slug":"databasejoinmeta-exception-errorobtainingfields","errorCode":null,"errorMessage":"DatabaseJoinMeta.Exception.ErrorObtainingFields","messagePattern":"DatabaseJoinMeta\\.Exception\\.ErrorObtainingFields","errorType":"exception","errorClass":"KettleStepException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/trans/steps/databasejoin/DatabaseJoinMeta.java","lineNumber":320,"sourceCode":"      for ( int i = 0; i < add.size(); i++ ) {\n        ValueMetaInterface v = add.getValueMeta( i );\n        v.setOrigin( name );\n      }\n      row.addRowMeta( add );\n    } else {\n      // No cache hit, connect to the database, do it the hard way...\n      //\n      try {\n        db.connect();\n        add = db.getQueryFields( space.environmentSubstitute( sql ), true, param, new Object[param.size()] );\n        for ( int i = 0; i < add.size(); i++ ) {\n          ValueMetaInterface v = add.getValueMeta( i );\n          v.setOrigin( name );\n        }\n        row.addRowMeta( add );\n        db.close();\n      } catch ( KettleDatabaseException dbe ) {\n        throw new KettleStepException( BaseMessages.getString(\n          PKG, \"DatabaseJoinMeta.Exception.ErrorObtainingFields\" ), dbe );\n      }\n    }\n  }\n\n  @Override\n  public String getXML() {\n    StringBuilder retval = new StringBuilder( 300 );\n\n    retval\n      .append( \"    \" ).append(\n        XMLHandler.addTagValue( \"connection\", databaseMeta == null ? \"\" : databaseMeta.getName() ) );\n    retval.append( \"    \" ).append( XMLHandler.addTagValue( \"rowlimit\", rowLimit ) );\n    retval.append( \"    \" ).append( XMLHandler.addTagValue( \"sql\", sql ) );\n    retval.append( \"    \" ).append( XMLHandler.addTagValue( \"outer_join\", outerJoin ) );\n    retval.append( \"    \" ).append( XMLHandler.addTagValue( \"replace_vars\", replacevars ) );\n    retval.append( \"    <parameter>\" ).append( Const.CR );\n    for ( int i = 0; i < parameterField.length; i++ ) {","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/steps/databasejoin/DatabaseJoinMeta.java#L302-L338","documentation":"In DatabaseJoinMeta.getFields(), after successfully obtaining the query field row metadata, the code copies fields, sets origins, closes the DB, and wraps any KettleDatabaseException raised in that block as KettleStepException 'DatabaseJoinMeta.Exception.ErrorObtainingFields'. Unlike error 1318, the query metadata was fetched but the post-processing/cleanup of the connection or row copy failed.","triggerScenarios":"getFields when db.close() or the row-copy/setOrigin loop throws a KettleDatabaseException — typically a connection failure during close, or an already-closed/broken connection in the cached query-fields path.","commonSituations":"Database connection dropped between getQueryFields and close (network timeout, DB restart); connection pool invalidation; a connection object shared across threads being closed twice.","solutions":["Check the chained cause dbe for the concrete JDBC failure during the field-copy/close phase.","Test the database connection and network stability; re-run the transformation.","Avoid sharing the Database object across threads; get a fresh connection per call.","Update the JDBC driver if the stack trace points at driver-level close/connection errors."],"exampleFix":"// before: shared Database reused after close elsewhere\ndb.close();\n// after: ensure single-owner lifecycle\nif (db != null && !db.isClosed()) { db.close(); }","handlingStrategy":"retry","validationCode":"try (Connection c = dataSource.getConnection()) {\n  if (!c.isValid(5)) throw new IllegalStateException(\"DB connection invalid before getFields\");\n}","typeGuard":null,"tryCatchPattern":"int attempts = 0;\nwhile (attempts < 3) {\n  try { meta.getFields(...); break; }\n  catch (KettleStepException e) {\n    if (!e.getMessage().contains(\"ErrorObtainingFields\") || ++attempts == 3) throw e;\n    Thread.sleep(1000L * attempts); // retry on transient connection loss\n  }\n}","preventionTips":["Keep DB connections idle-timeout free during initialization","Avoid sharing Database objects across threads","Keep JDBC drivers current for stable connection close behavior"],"tags":["kettle","database","connection"],"backgroundTag":"database-query-failed","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"}