pentaho/pentaho-kettle · error · KettleException

Unable to save repository element [ + repositoryElement + ]…

Error message

Unable to save repository element [ + repositoryElement + ] to XML file : + calcFilename( repositoryElement )

What it means

This wraps any unexpected exception thrown while persisting a repository element to its XML file in KettleFileRepository.save(). The original exception is chained; the message includes the element and the calculated target filename via calcFilename(). It is the generic save-failure path covering IO errors, VFS problems, and serialization bugs.

Solutions

  1. Inspect the chained cause 'Caused by' — it identifies the real IO/serialization problem.
  2. Verify the file repository base directory exists and is writable by the process user.
  3. Confirm calcFilename()'s computed path is valid (watch for illegal characters in element names).
  4. Check disk space and that the VFS provider for the repo URL is correctly configured.

Example fix

// before: base dir may not exist
KettleEnvironment.init();
repo.connect("admin", "admin");

// after: ensure base directory exists and is writable before saving
File base = new File(repoBaseDir);
if (!base.exists()) base.mkdirs();
if (!base.canWrite()) throw new IllegalStateException("Repo dir not writable: " + base);
repo.save(transMeta, null, null);
Defensive patterns

Strategy: try-catch

Validate before calling

// verify repo base is writable before saving
if (!Files.isWritable(Paths.get(repoBaseDir))) throw new IllegalStateException("Repo not writable");

Try / catch

try {
  repo.save(element, null, null);
} catch (KettleException e) {
  if (e.getMessage().startsWith("Unable to save repository element")) {
    log.error("Save failed for " + e.getCause(), e); // real cause is chained
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling save() when the destination file cannot be written — path invalid or too long, missing directory, disk full, permission denied, VFS misconfiguration, or the element's getXML()/serialization throws.

Common situations: File repository base directory pointing to a nonexistent or read-only location; antivirus/file locks on Windows; running PDI as a user without write access to the repo folder; network-mounted repo path that dropped.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/repository/filerep/KettleFileRepository.java:347

      // If so, we need to now remove the old file to prevent us from having multiple copies.
      //
      if ( repositoryElement.getObjectId() != null && !repositoryElement.getObjectId().equals( objectId ) ) {
        delObject( repositoryElement.getObjectId() );
      }

      repositoryElement.setObjectId( objectId );

      // Finally, see if there are external objects that need to be stored or updated in the MetaStore
      //
      if ( repositoryElement instanceof TransMeta ) {
        ( (TransMeta) repositoryElement ).saveMetaStoreObjects( this, metaStore );
      }
      if ( repositoryElement instanceof JobMeta ) {
        ( (JobMeta) repositoryElement ).saveMetaStoreObjects( this, metaStore );
      }

    } catch ( Exception e ) {
      throw new KettleException( "Unable to save repository element ["
        + repositoryElement + "] to XML file : " + calcFilename( repositoryElement ), e );
    }
  }

  @Override
  public RepositoryDirectoryInterface createRepositoryDirectory( RepositoryDirectoryInterface parentDirectory,
    String directoryPath ) throws KettleException {
    String folder = calcDirectoryName( parentDirectory );
    String newFolder = folder;
    if ( folder.endsWith( "/" ) ) {
      newFolder += directoryPath;
    } else {
      newFolder += "/" + directoryPath;
    }

    FileObject parent = KettleVFS.getInstance( DefaultBowl.getInstance() ).getFileObject( newFolder );
    try {
      parent.createFolder();

View on GitHub (pinned to f3058517a1)