Salesforce CLI TypeError with node v. 25.1.0

This morning I kept getting "TypeError: Cannot read properties of undefined (reading 'prototype')" when trying to run any command with the Salesforce CLI. The version of node currently used is listed when you run a sf --version like below.

sf --version
@salesforce/cli/2.110.22 darwin-arm64 node-v25.1.0

After a bit back and forth the solution was to downgrade node.js from v. 25.1.0 to v. 24. I’m using homebrew so installing v. 24, and making the Salesforce CLI use it, was pretty straight forward once I knew (the brew link is required as npm will most likely use the newest version).

brew install node@24
brew link node@24 --overwrite

Once installed, run sf --version and it should show v. 24 like so.

sf --version
@salesforce/cli/2.110.22 darwin-arm64 node-v24.11.0

Once this was done the Salesforce CLI commands works just fine.

Add language to Salesforce CLI scratch org definition from terminal

All my scratch orgs gets created with the user-language set to Danish which is a good guess but I cannot find anything in Setup that way. No Dev Hub setting I’ve found can change that but setting the language in the scratch org definition file will. That’s easily done from the terminal with jq.

cat config/project-scratch-def.json | jq '. += {"language": "en_US"}' > config/project-scratch-def-en_US.json

Note to self – script to update all my CLI stuff

npm_update_all_global() {
   npm list -g --json | jq ".dependencies | keys[]" -r | while read line; do
      npm -g update "$line"
   done
}
brew_update_all() {
   brew update && brew upgrade
}
heroku_update_all() {
   heroku update
}
sfdx_update_all() {
   sfdx update
}
lekkim_update_all_cli() {
   npm_update_all_global
   brew_update_all
   heroku_update_all
   sfdx_update_all
}

Unable to force:source:push ExperienceBundle from API version 50.0 with API version 51.0

Yesterday I was trying to deploy some source including an Experience Cloud Site (using ExperienceBundle) created with API version 50.0 to a scratch org but it failed when I updated the API version to 51.0 in sfdx-project.json . Deploying with API version 50.0 worked just fine. The deploy (w/ API version 51.0) failed with the following message:

Error  force-app/main/default/experiences/Digital_Capability_Assessment_Aura1.site-meta.xml  You seem to be missing the property configurationTags in Digital_Capability_Assessment_Aura1/routes/register.json with component ID: f7c5ea49-0bde-4848-a72f-82ace4ea6760

And the error message was right – that key is not in the file but that’s was also true for some of the other Experience Cloud files. Deploying with API version 50.0 and trying to pull with API version 51.0 didn’t change any source. Clever people told me that this was expected as nothing changed in the org (regardless of a publish or similar).

Solution was to deploy with force:source:push with API version 50.0 and then do a force:source:retrieve specifying API version 51.0 on the command line. Then afterwards toggle the API version in sfdx-project.json.

sfdx force:source:retrieve -u dca_scratch_comm -a 51.0 -m ExperienceBundle

Turning on trace debugging with the Salesforce CLI

When using the Salesforce CLI as the primary way to interact with a scratch org turning on Apex trace debugging can be a tiresome-I-have-to-click-into-the-org situation. Usually you are not able to use force:apex:log:list and force:apex:log:get commands to work with Apex logs without first opening the Web Developer in the org or setting up trace logging.

But it turns out there is a better way using a simple script.

The below script does the trick for you can easily be added to your process for creating new scratch orgs. The script sets up some timestamps and then queries for the scratch org user userId. Then we get the Id of the trace log configuration for the user and then updates the record to enable trace logging for 24 hours.

NOW=`date -u +"%Y-%m-%dT%H:%M:%SZ"`
EXP=`date -v+24H -u +"%Y-%m-%dT%H:%M:%SZ"`

USERID=`sfdx force:data:soql:query -q "select id,name from user where name='User User'" --json | jq ".result.records[0].Id" -r`

TRACEID=`sfdx force:data:soql:query --query "SELECT Id, DebugLevel.DeveloperName, ExpirationDate, TracedEntityId FROM Traceflag WHERE TracedEntityId IN (SELECT ID from USER WHERE ID = '${USERID}')" --usetoolingapi --json | jq ".result.records[0].Id" -r`

sfdx force:data:record:update --sobjecttype TraceFlag --sobjectid $TRACEID -v "StartDate=$NOW ExpirationDate=$EXP" --usetoolingapi --json --loglevel fatal

The above script uses jq (https://stedolan.github.io/jq/) for JSON parsing and works on Mac (date command switches is slightly different on Linux) so YMMV.

Individual Object enablement in Salesforce Scratch orgs – now available!

I’m so happy that one of my scratch org pet peeves finally made it into the product for the Spring 19 release. It has not been possible to enable the Individual Object when creating new scratch orgs and IMO this has been lacking and a cause for many issues, discussions and workarounds in customer projects. But all this is a thing of the past now! Starting Spring 19 you may add the below orgPreferenceSetting to your scratch org definition file:

"settings": {
    "orgPreferenceSettings": {
      "consentManagementEnabled": true
    }
 }

Now the setting hasn’t made it into the documentation as of this writing but I know that it’s in the works. No go and create scratch orgs with the Individual Object enabled!

Further reading:

Calling a Swagger service from Apex using openapi-generator

For a demo I needed to make calls to the Petstore API using a Swagger / OpenAPI definition from Apex. Unfortunately the Apex support that External Services in Salesforce provides is only callable from Flows so I needed another approach. Using openapi-generator and installing via Homebrew it went relatively smoothly anyway.

It’s always nice to stand on the shoulder of giants so the examples provided by Rene Winkelmeyer (Connecting to Swagger-backed APIs with Clicks or Code ) came in handy. Unfortunately didn’t work as the prefix for the generated classes was different plus the petId was was 1 and not 100. Anyway again easy to fix.

Below are my steps to get working using Homebrew and Salesforce DX.

$ brew install openapi-generator
$ openapi-generator generate \
-i https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/2_0/petstore.yaml \
-g apex \
-o /tmp/petstore_swagger/
$ cd /tmp/petstore_swagger
$ sfdx force:org:create \
-v my_devhub \
-f config/project-scratch-def.json -a petstore_swagger
$ sfdx force:source:push -u petstore_swagger
$ sfdx force:apex:execute -u petstore_swagger
>> Start typing Apex code. Press the Enter key after each line,
>> then press CTRL+D when finished.
OASClient client = new OASClient();
OASPetApi api = new OASPetApi(client);
Map<String,Object> params = new Map<String, Object>();
Long petId = Long.valueOf('1');
params.put('petId', petId);
OASPet pet = api.getPetById(params);
System.debug(pet);
<Ctrl-D>

jq and multi-field output to CSV

jq is some of the most underrated tools out there I think. It’s a command line JSON parser that makes it super easy to work with JSON on the command line and in turn makes developing small SalesforceDX tools a breeze. Today I needed to generate a CSV file of all fields from different objects for the integration team that doesn’t have access to Salesforce. Doing the describe is easy using the Salesforce REST API but when using jq different are usually on different lines like below (-r is a nifty switch for getting raw, unquoted strings).

$ sfdx force:schema:sobject:describe -s Account -u myorg --json | jq -r ".result.fields[] | .label, .name"
Age
Age__pc
PO Box
PO_Box__pc
Postal Code Before City
Postal_Code_Before_City__pc
Street No Before Street
Street_No_Before_Street__pc
Archived State
Archived_State__pc

The output is almost what I wanted but really wanted not  to have to edit the file manually to build the output. Some quick googling and it appears that jq supports both CSV and tabular output from arrays. So fixing the issue was as simple as follows:

$ sfdx force:schema:sobject:describe -s Account -u myorg --json | jq -r ".result.fields[] | [.label, .name] | @csv"
"Age","Age__pc"
"PO Box","PO_Box__pc"
"Postal Code Before City","Postal_Code_Before_City__pc"
"Street No Before Street","Street_No_Before_Street__pc"
"Archived State","Archived_State__pc"

This is so cool… Love it!!

How to add missing SalesforceDX org alias

If you ever added a non-scratch org in SalesforceDX but forgot to add an alias using –alias / -a or simply want to change an alias there is a way easier way when readding all orgs. Simply edit the alias.json file as below.

  1. Locate your SalesforceDX settings directory (on Mac that is in ~/.sfdx and on Windows it seems to be %USERPROFILE%\.sfdx)
  2. Edit the alias.json file (please note that it uses Unix style endings so use an appropriate editor)
  3. Add missing alias mapping or correct incorrect mappings. The file simply maps an alias to the org username shown in “sfdx force:org:list”. Below is an example file.
{
 "orgs": {
   "example.qa": "mheisterberg@example.com.qa",
   "example.uat": "mheisterberg@example.com.uat"
 }
}

Bash one-liner for Apex test coverage percentage using SalesforceDX

Update 3 May 2018: There are issues with the percentages reported by SalesforceDX plus it doesn’t report coverage on classes with 0% coverage which will shrew the results. The approach outlined above can be used as an indication but cannot as of today be used as a measure for code coverage when it comes to production deployments. As an example I’ve had the above snippet report a coverage of 88% where as a production deploy reported 63% coverage. We – Salesforce – are aware of the issue and are working to resolve it. Stay tuned!

Note to self – quick note on how to run all tests in a connected org (as identified by the -u argument) and use jq and awk to grab the overall test coverage percentage.

$ sfdx force:apex:test:run -u mheisterberg@example.com.appdev -c -w 2 -r json | jq -r ".result.coverage.coverage[].coveredPercent" | awk '{s+=$1;c++} END {print s/c}'
> 88.1108

YMMV!