Skip to content

project convert source double-escapes XML numeric character references (' → ') breaking MDAPI deploy of formulas/defaultValues #3543

@stPaulBaSa

Description

@stPaulBaSa

Summary

sf project convert source double-escapes XML numeric character references inside <formula>, <defaultValue>, and (by extension) <errorConditionFormula> tags. A source file containing &#39; (valid XML for ') becomes &amp;#39; in the generated .object. The result deploys a broken formula string to the org and also causes server-side Syntax error. Found '&' failures in pipelines that consume the converted MDAPI.

Steps To Reproduce

Minimal project (no org needed). One field with a formula that uses both &amp; (string concat) and &#39; (single-quote).

sfdx-project.json:

{
  "packageDirectories": [{"path": "force-app", "default": true}],
  "sourceApiVersion": "59.0"
}

force-app/main/default/objects/Foo__c/Foo__c.object-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
    <deploymentStatus>Deployed</deploymentStatus>
    <label>Foo</label>
    <pluralLabel>Foos</pluralLabel>
    <nameField><label>Name</label><type>Text</type></nameField>
    <sharingModel>ReadWrite</sharingModel>
</CustomObject>

force-app/main/default/objects/Foo__c/fields/Bar__c.field-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Bar__c</fullName>
    <externalId>false</externalId>
    <formula>LOWER(Name &amp; &#39; &#39; &amp; Name)</formula>
    <formulaTreatBlanksAs>BlankAsZero</formulaTreatBlanksAs>
    <label>Bar</label>
    <required>false</required>
    <type>Text</type>
    <unique>false</unique>
</CustomField>

Run:

sf project convert source --source-dir force-app --output-dir mdapi
grep formula mdapi/objects/Foo__c.object

Expected result

The emitted <formula> should preserve valid XML entities that already existed in the source (or re-encode them losslessly):

<formula>LOWER(Name &amp; &#39; &#39; &amp; Name)</formula>

Actual result

The numeric character references get escaped a second time, producing:

<formula>LOWER(Name &amp; &amp;#39; &amp;#39; &amp; Name)</formula>

After the server-side XML decode, the formula body stored on the field becomes literally:

LOWER(Name & &#39; &#39; & Name)

…which is not a valid formula. In a real pipeline running sf project deploy start against an org (source-format deploy, REST transport), this surfaces as MDAPI component failures like:

  • 30 component failures in one deploy, all Syntax error. Found '&' (line:col)
  • Every failing component is a CustomField with a <formula> / <defaultValue> that contains &#39;, or a ValidationRule with <errorConditionFormula>. Examples from a real deploy:
    • CustomField Action_Item__c.Due_Status__c Found '&' (174:13)
    • CustomField Activity__c.Action_Verb__c Found '&' (1784:13)
    • CustomField Favorite__c.Type__c Found '&' (103:13)
    • ValidationRule Activity_Template__c.Activity_Type_Field_Required Found '&' (1021:22)
    • CustomField SiteTraker_Object__mdt.History_Object_Name__c (defaultValue <code>&#39;Project_History__c&#39;</code>) Found '&' (309:13)

Additional information

  • Reproduces identically on @salesforce/cli@2.132.13 and @salesforce/cli@2.132.14 (same @salesforce/source-deploy-retrieve 12.32.8 / 12.32.9 respectively). I originally suspected the 2.132.14 publish based on pipeline timing, but the local repro shows the same double-encoding on both, so the regression may predate that.
  • sf project convert source and sf project deploy start share the source→MDAPI path via @salesforce/source-deploy-retrieve; the bug manifests wherever that path is exercised.

System Information

Shell: bash on macOS (Darwin 24.6.0)

{
  "architecture": "darwin-arm64",
  "cliVersion": "@salesforce/cli/2.132.14",
  "nodeVersion": "node-v21.7.0",
  "osVersion": "Darwin 24.6.0",
  "shell": "bash",
  "pluginVersions": [
    "@salesforce/cli 2.132.14 (core)",
    "deploy-retrieve 3.24.33 (core)"
  ]
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIssue or pull request that identifies or fixes a bugvalidatedVersion information for this issue has been validated

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions