Summary
A Salesforce DX project using decomposePermissionSetBeta2 can fail during source-to-Metadata API conversion when a decomposed Permission Set is resolved from a package directory or manifest.
The resolver can select a child fragment as the Permission Set root. The Permission Set transformer then generates a non-existent synthetic path.
Environment
- Salesforce CLI:
2.146.3
@salesforce/plugin-deploy-retrieve: 13.0.1
- Node.js:
24.16.0
- OS: Windows
- Source behavior:
decomposePermissionSetBeta2
The deploy-retrieve plugin declares @salesforce/source-deploy-retrieve with dependency range ^12.32.9.
Reproduction
sfdx-project.json:
{
"packageDirectories": [
{
"path": "force-app",
"default": true
}
],
"sourceBehaviorOptions": [
"decomposePermissionSetBeta2"
],
"sourceApiVersion": "63.0"
}
Source tree:
force-app/main/default/permissionsets/MyPermissionSet/
|-- MyPermissionSet.permissionset-meta.xml
|-- MyPermissionSet.applicationVisibility-meta.xml
|-- MyPermissionSet.classAccess-meta.xml
`-- objectSettings/
`-- Account.objectSettings-meta.xml
Manifest:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>MyPermissionSet</members>
<name>PermissionSet</name>
</types>
<version>63.0</version>
</Package>
Command:
sf project deploy start \
--manifest manifest/package.xml \
--ignore-conflicts \
--dry-run
Actual result
The deployment fails before Metadata API execution:
ConversionError: ENOENT: no such file or directory, open
.../permissionsets/MyPermissionSet/objectSettings/Account.permissionset-meta.xml
Expected result
The resolver should identify the parent file:
MyPermissionSet.permissionset-meta.xml
and include all decomposed child fragments in the conversion.
Technical cause
BaseSourceAdapter.parseAsRootMetadataXml() considers a metadata file to be a root when the containing directory name matches the parsed full name.
A child such as MyPermissionSet.applicationVisibility-meta.xml has the same logical name as its parent directory and can therefore be incorrectly classified as the root.
When the root lookup is retried, TreeContainer.find() compares base names without considering the metadata suffix. Files such as MyPermissionSet.classAccess-meta.xml and MyPermissionSet.permissionset-meta.xml are therefore indistinguishable and the first matching file can be returned.
DecomposedPermissionSetTransformer then receives the wrong XML path and constructs the invalid fallback path:
Account.objectSettings-meta.xml
-> Account.permissionset-meta.xml
Proposed resolution
- Require the metadata suffix to match the parent type suffix or legacy suffix before accepting a file as the root.
- Resolve the root file in
ownFolder components by matching the parent suffix, not only the base name.
- Add a regression test for a Beta2 Permission Set resolved from its directory.
Validation
The fix is implemented in fork commit:
MaxHuet/source-deploy-retrieve@83ce8bc05
Validation results:
yarn build: passed
yarn test:only: 1,075 tests passed
yarn test:snapshot: 79 tests passed
- Real decomposed Permission Set resolution: passed
- Real Permission Set recomposition to Metadata API format: passed
The existing registry completeness test reports unrelated metadata coverage entries missing from the current registry.
Summary
A Salesforce DX project using
decomposePermissionSetBeta2can fail during source-to-Metadata API conversion when a decomposed Permission Set is resolved from a package directory or manifest.The resolver can select a child fragment as the Permission Set root. The Permission Set transformer then generates a non-existent synthetic path.
Environment
2.146.3@salesforce/plugin-deploy-retrieve:13.0.124.16.0decomposePermissionSetBeta2The deploy-retrieve plugin declares
@salesforce/source-deploy-retrievewith dependency range^12.32.9.Reproduction
sfdx-project.json:{ "packageDirectories": [ { "path": "force-app", "default": true } ], "sourceBehaviorOptions": [ "decomposePermissionSetBeta2" ], "sourceApiVersion": "63.0" }Source tree:
Manifest:
Command:
Actual result
The deployment fails before Metadata API execution:
Expected result
The resolver should identify the parent file:
and include all decomposed child fragments in the conversion.
Technical cause
BaseSourceAdapter.parseAsRootMetadataXml()considers a metadata file to be a root when the containing directory name matches the parsed full name.A child such as
MyPermissionSet.applicationVisibility-meta.xmlhas the same logical name as its parent directory and can therefore be incorrectly classified as the root.When the root lookup is retried,
TreeContainer.find()compares base names without considering the metadata suffix. Files such asMyPermissionSet.classAccess-meta.xmlandMyPermissionSet.permissionset-meta.xmlare therefore indistinguishable and the first matching file can be returned.DecomposedPermissionSetTransformerthen receives the wrong XML path and constructs the invalid fallback path:Proposed resolution
ownFoldercomponents by matching the parent suffix, not only the base name.Validation
The fix is implemented in fork commit:
MaxHuet/source-deploy-retrieve@83ce8bc05
Validation results:
yarn build: passedyarn test:only: 1,075 tests passedyarn test:snapshot: 79 tests passedThe existing registry completeness test reports unrelated metadata coverage entries missing from the current registry.