pentaho/pentaho-kettle · error · KettleException
PurRepository.fileCannotBeSavedInRootDirectory
PurRepository.fileCannotBeSavedInRootDirectory
Error message
PurRepository.fileCannotBeSavedInRootDirectory (localized, takes element name+extension; cannot be saved in root directory)
What it means
The PUR repository forbids saving transformation/job files directly in the repository root directory ('/'), because the UI has no way to read them back. The localized message includes the element name plus extension. This is an explicit pre-save path validation enforced before any network call.
Solutions
- Set a non-root directory on the element before saving: element.setRepositoryDirectory(new RepositoryDirectory(...)) or a real folder in the repository
- Save via 'Save As' in Spoon into a subfolder instead of the root
- Validate the target directory before invoking save and reject or relocate root-directory saves
Example fix
// before transMeta.setRepositoryDirectory(rootDir); // "/" repository.save(transMeta, "v", null, true); // after RepositoryDirectory sub = new RepositoryDirectory(rootDir, "etl"); transMeta.setRepositoryDirectory(sub); repository.save(transMeta, "v", null, true);
Defensive patterns
Strategy: validation
Validate before calling
if (Import.ROOT_DIRECTORY.equals(element.getRepositoryDirectory().toString())) {
throw new KettleException("Assign a subdirectory before saving: " + element.getName());
} Try / catch
try {
repository.save(element, comment, null, true);
} catch (KettleException e) {
if (e.getMessage() != null && e.getMessage().contains("cannot be saved in root")) {
element.setRepositoryDirectory(someSubdirectory);
repository.save(element, comment, null, true);
} else throw e;
} Prevention
- Always call setRepositoryDirectory with a real folder before save
- Never leave RepositoryDirectory at its default root in generated metadata
- Validate target path in save helper utilities
When it happens
Trigger: Calling PurRepository.save / saveRepositoryObject where element.getRepositoryDirectory().toString() equals Import.ROOT_DIRECTORY ("/"), i.e. the element has no subdirectory assigned.
Common situations: Programmatically generated transformations saved without calling setRepositoryDirectory; importing jobs/trans into '/' via scripts or the Java API; default/uninitialized RepositoryDirectory in freshly built metadata.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- AbsSecurityProvider.ERROR_0002_UNABLE_TO_ACCESS_IS_ALLOWED
- AddSequenceMeta.Exception.UnableToReadStepInfo
- AddSequenceMeta.Exception.UnableToSaveStepInfo
- Error loading job details
- File is currently locked by another user for editing
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/e9d014eac3f1c1aa.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/pur/core/src/main/java/org/pentaho/di/repository/pur/PurRepository.java:3441
case JOB:
objectTransformer = jobDelegate;
break;
default:
throw new KettleException(
"Unknown RepositoryObjectType. Should be TRANSFORMATION or JOB " );
}
saveTransOrJob( objectTransformer, element, versionComment, versionDate, saveSharedObjects, checkLock, checkRename,
loadRevision, checkDeleted );
}
protected void saveTransOrJob( ISharedObjectsTransformer objectTransformer, RepositoryElementInterface element,
String versionComment, Calendar versionDate,
boolean saveSharedObjects,
boolean checkLock, boolean checkRename,
boolean loadRevision, boolean checkDeleted ) throws KettleException {
if ( Import.ROOT_DIRECTORY.equals( element.getRepositoryDirectory().toString() ) ) {
// We don't have possibility to read this file via UI
throw new KettleException( BaseMessages.getString( PKG, "PurRepository.fileCannotBeSavedInRootDirectory",
element.getName() + element.getRepositoryElementType().getExtension() ) );
}
ExtensionPointHandler.callExtensionPoint( log, KettleExtensionPoint.BeforeSaveToRepository.id, element );
final boolean isUpdate = ( element.getObjectId() != null && element.getObjectId().getId() != null );
RepositoryFile file = null;
if ( isUpdate ) {
readWriteLock.readLock().lock();
try {
ObjectId id = element.getObjectId();
file = pur.getFileById( id.getId() );
if ( checkLock && file.isLocked() && !unifiedRepositoryLockService.canUnlockFileById( id ) ) {
throw new KettleException( "File is currently locked by another user for editing" );
}
if ( checkDeleted && isInTrash( file ) ) {View on GitHub (pinned to f3058517a1)