pentaho/pentaho-kettle · error · IllegalStateException

S3FileOutput.Error.S3ClientInitFailed

Error message

S3FileOutput.Error.S3ClientInitFailed

What it means

processS3Path throws IllegalStateException with message key 'S3FileOutput.Error.S3ClientInitFailed' when the S3 provider returns a null AmazonS3 client after prepare(). A null client means the AWS SDK client could not be constructed, typically because credentials or region configuration are missing/invalid.

Solutions

  1. Set valid AWS access key and secret key in the S3 File Output step (or fix the credentials source: env vars, profile, IAM role).
  2. Verify the AWS region is configured and correct for the bucket.
  3. Check that s3Provider.prepare() gets complete S3Details (bucket, path, credentials, variables resolved) before requesting the client.
  4. Test credentials with AWS CLI (aws sts get-caller-identity) on the same host to rule out environment issues.

Example fix

// before
AmazonS3 s3Client = s3Provider.getS3Client( s3Details );
if ( s3Client == null ) {
  throw new IllegalStateException( BaseMessages.getString( PKG, "S3FileOutput.Error.S3ClientInitFailed" ) );
}
// after: fail fast with a clearer message when credentials are absent
if ( s3Details.getAccessKey() == null || s3Details.getSecretKey() == null ) {
  throw new IllegalStateException( "AWS credentials are not configured for the S3 File Output step" );
}
AmazonS3 s3Client = s3Provider.getS3Client( s3Details );
Defensive patterns

Strategy: validation

Validate before calling

// verify credentials before invoking processS3Path
boolean credsReady = awsAccessKey != null && !awsAccessKey.isEmpty()
  && awsSecretKey != null && !awsSecretKey.isEmpty();
if ( !credsReady ) throw new IllegalStateException( "Configure AWS access/secret keys before running" );

Try / catch

try {
  helper.processS3Path( transMeta, params, path );
} catch ( IllegalStateException e ) {
  logError( "S3 client init failed: " + e.getMessage() );
}

Prevention

When it happens

Trigger: listS3ContentsAction or testProcessS3PathSuccess -> processS3Path where createS3DetailsFromParams yields credentials (access key/secret) that s3Provider.prepare()/getS3Client() reject, so getS3Client returns null instead of a client.

Common situations: Empty or malformed AWS access/secret keys in the step dialog; environment without AWS credentials and no keys configured; wrong region name; IAM profile not available on the host; AWS SDK configuration changes across plugin versions.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/8db853c86beb9322. Report an issue: GitHub.

Appendix: source

Thrown at plugins/s3-vfs/core/src/main/java/org/pentaho/amazon/s3/S3FileOutputHelper.java:295

    } catch ( Exception e ) {
      response.put( ERROR,
          BaseMessages.getString( PKG, "S3FileOutput.Error.ListObjectsFailed" ) );
      response.put( ACTION_STATUS, FAILURE_RESPONSE );
    }
    return response;
  }

  public void processS3Path( TransMeta transMeta,
                             Map<String, String> queryParams,
                             String path,
                             JSONArray contents ) throws KettleException {
    String s3Prefix = S3FileProvider.SCHEME + "://";
    S3Details s3Details = createS3DetailsFromParams( transMeta, queryParams );
    s3Details.setSpace( new Variables() );
    s3Details = s3Provider.prepare( s3Details );
    AmazonS3 s3Client = s3Provider.getS3Client( s3Details );
    if ( s3Client == null ) {
      throw new IllegalStateException(
          BaseMessages.getString( PKG, "S3FileOutput.Error.S3ClientInitFailed" )
      );
    }
    String cleanPath = path.replace( s3Prefix, "" );
    String bucketName = cleanPath.contains( S3FileName.DELIMITER )
        ? cleanPath.substring( 0, cleanPath.indexOf( S3FileName.DELIMITER ) )
        : cleanPath;
    String prefix = cleanPath.contains( S3FileName.DELIMITER )
        ? cleanPath.substring( cleanPath.indexOf( S3FileName.DELIMITER ) + 1 )
        : "";
    if ( !prefix.isEmpty() && !prefix.endsWith( S3FileName.DELIMITER ) ) {
      prefix += S3FileName.DELIMITER;
    }

    ListObjectsV2Request listRequest = new ListObjectsV2Request()
        .withBucketName( bucketName )
        .withPrefix( prefix )
        .withDelimiter( S3FileName.DELIMITER );

View on GitHub (pinned to f3058517a1)