{"record":{"id":"f260082063859e96","repo":"pentaho/pentaho-kettle","slug":"provider-file-name-s-is-not-a-descendant-of-the-connection","errorCode":null,"errorMessage":"Provider file name '%s' is not a descendant of the connection root '%s'.","messagePattern":"Provider file name '(.+?)' is not a descendant of the connection root '(.+?)'\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/connections/vfs/DefaultVFSConnectionFileNameTransformer.java","lineNumber":197,"sourceCode":"\n  // region toPvfsFileName\n  @NonNull\n  @Override\n  public ConnectionFileName toPvfsFileName( @NonNull FileName providerFileName, @NonNull T details )\n    throws KettleException {\n    // Determine the part of provider file name following the connection \"root\".\n    // Use the transformer to generate the connection root provider uri.\n    // Both uris are assumed to be normalized.\n    // Examples:\n    // - connectionRootProviderUri: \"hcp://domain.my:443/root/path/\"           |  \"s3://\" |  \"local://\"\n    // - providerUri:               \"hcp://domain.my:443/root/path/rest/path\"  |  \"s3://rest/path\"\n    // Example: \"pvfs://my-connection\"\n\n    String connectionRootProviderUri = getConnectionRootProviderUriPrefix( details );\n    String providerUri = providerFileName.getURI();\n\n    if ( !connectionFileNameUtils.isDescendantOrSelf( providerUri, connectionRootProviderUri ) ) {\n      throw new IllegalArgumentException(\n        String.format(\n          \"Provider file name '%s' is not a descendant of the connection root '%s'.\",\n          providerUri,\n          connectionRootProviderUri ) );\n    }\n\n    String restUriPath = providerUri.substring( connectionRootProviderUri.length() );\n\n    // Examples: \"/rest/path\" or \"rest/path\"\n\n    return buildPvfsFileName( details, restUriPath, providerFileName.getType() );\n  }\n\n  /**\n   * Builds a PVFS file name for a connection given the URI path and the file type.\n   * <h3>Implementation Notes</h3>\n   * <p>\n   * {@code nonNormalizedRestPath} may be using the URI encoding, resulting from {@link FileName#getURI()} (which is","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/connections/vfs/DefaultVFSConnectionFileNameTransformer.java#L179-L215","documentation":"DefaultVFSConnectionFileNameTransformer.toPvfsFileName converts a provider file name into a PVFS file name scoped to a connection. Before transforming, it verifies the provider URI lies within the connection's root provider URI prefix (isDescendantOrSelf). If the file name points outside the connection root, the transformation would produce a PVFS name that escapes the connection namespace, so an IllegalArgumentException is thrown.","triggerScenarios":"Calling toPvfsFileName with a provider FileName whose URI is not under the connection root URI returned by getConnectionRootProviderUriPrefix(details) — e.g. the file belongs to a different connection, the root is 'pvfs://conn-a' but the file resolves under 'pvfs://conn-b', or the details object was mutated/renamed after the FileName was created.","commonSituations":"Copy-pasting connection details between connections; renaming a VFS connection while holding FileName objects created under the old root; resolving a file through the wrong ConnectionManager key; code that builds provider URIs by string concatenation and accidentally produces a sibling root.","solutions":["Ensure the FileName passed to toPvfsFileName was obtained through the same VFSConnectionDetails/connection whose transformer is being used.","Verify the provider URI starts with the connection root prefix (call isDescendantOrSelf yourself first) before transforming.","If the file belongs to another connection, look up the correct VFSConnectionDetails and use its transformer.","Check for stale FileName objects held across connection renames; re-resolve them."],"exampleFix":"// before\ntransformer.toPvfsFileName( wrongDetails, providerFileName );\n// after\nif ( !fileNameUtils.isDescendantOrSelf( providerFileName.getURI(), transformer.getConnectionRootProviderUriPrefix( details ) ) ) {\n  providerFileName = resolveFileForConnection( details, providerFileName );\n}\nString pvfs = transformer.toPvfsFileName( details, providerFileName );","handlingStrategy":"validation","validationCode":"if ( !fileNameUtils.isDescendantOrSelf( providerFileName.getURI(), transformer.getConnectionRootProviderUriPrefix( details ) ) ) {\n  throw new IllegalStateException( \"File is outside connection root; resolve via the correct connection\" );\n}","typeGuard":"boolean inConnectionRoot( String uri, String root ) {\n  return uri != null && root != null && uri.startsWith( root );\n}","tryCatchPattern":"try {\n  String pvfs = transformer.toPvfsFileName( details, providerFileName );\n} catch ( IllegalArgumentException e ) {\n  // re-resolve the file under the correct connection details\n}","preventionTips":["Always obtain FileName objects through the same connection details used for transformation","Re-resolve file names after any connection rename","Assert root containment in tests that build provider URIs manually"],"tags":["vfs","path-scope","argument-validation"],"backgroundTag":"invalid-argument-value","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"}