pentaho/pentaho-kettle · error · IOException

unable to use a symbolic link:

Error message

unable to use a symbolic link: 

What it means

CustomDataStoreFactory's constructor rejects a data directory that is a symbolic link. It canonicalizes the path first, then throws IOException 'unable to use a symbolic link: <dir>' if the canonical path is still a symlink, as a security measure against symlink attacks on the local Google Drive token/data cache.

Solutions

  1. Point the data directory at a real directory, not a symlink.
  2. Bind-mount or copy the target directory to a physical path and use that path.
  3. If a symlink is unavoidable, replace it (rm the link, mkdir a real dir) and move data over.

Example fix

// before
File dir = new File("/var/lib/app/drive-link"); // symlink
new CustomDataStoreFactory(dir);
// after
File dir = new File("/var/lib/app/drive").getCanonicalFile(); // real directory
new CustomDataStoreFactory(dir);
Defensive patterns

Strategy: validation

Validate before calling

File canonical = dir.getCanonicalFile();
if (java.nio.file.Files.isSymbolicLink(canonical.toPath())) {
  throw new IllegalStateException("data directory must not be a symlink: " + canonical);
}

Try / catch

try {
  new CustomDataStoreFactory(dataDirectory);
} catch (IOException e) {
  if (e.getMessage().startsWith("unable to use a symbolic link")) {
    // reconfigure to a real directory
  } else { throw e; }
}

Prevention

When it happens

Trigger: Constructing new CustomDataStoreFactory(dataDirectory) where dataDirectory (after getCanonicalFile()) is a symlink — e.g. pointing the cache dir at a symlinked folder, or /tmp-style dirs replaced by symlinks.

Common situations: Dev setups where the data directory is a symlink to another disk; container images where /tmp or the app dir is a symlink; hardening-sensitive environments where this check is intentional.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

Thrown at plugins/pentaho-googledrive-vfs/core/src/main/java/org/pentaho/googledrive/vfs/util/CustomDataStoreFactory.java:48

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.HashMap;
import java.util.List;
import java.util.Iterator;

public class CustomDataStoreFactory extends AbstractDataStoreFactory {

  private final File dataDirectory;

  public CustomDataStoreFactory( File dataDirectory ) throws IOException {
    dataDirectory = dataDirectory.getCanonicalFile();
    this.dataDirectory = dataDirectory;
    if ( IOUtils.isSymbolicLink( dataDirectory ) ) {
      throw new IOException( "unable to use a symbolic link: " + dataDirectory );
    } else if ( !dataDirectory.exists() && !dataDirectory.mkdirs() ) {
      throw new IOException( "unable to create directory: " + dataDirectory );
    }
  }

  protected <V extends Serializable> DataStore<V> createDataStore( String id ) throws IOException {
    return new CustomDataStore( this, this.dataDirectory, id );
  }

  static class CustomDataStore<V extends Serializable> extends AbstractDataStore<V> {
    private File dataFile = null;
    private File dataDirectory = null;
    private HashMap<String, byte[]> keyValueMap = Maps.newHashMap();
    private final Lock lock = new ReentrantLock();

    CustomDataStore( CustomDataStoreFactory dataStore, File dataDirectory, String id ) throws IOException {
      super( dataStore, id );
      this.dataDirectory = dataDirectory;

View on GitHub (pinned to f3058517a1)