1

I have a cloudpage which serves the purpose of ejecting records from existing journey(s). The credentials are stored in a BU Package DE, so are the records in an ejection DE. Since the exit contact api has a limit of 50 records per call, I am trying to batch it.

For some reason I am getting the Error Fetching Records: Unable to retrieve security descriptor for this frame while batching records error when I first tried batching in small quantities(just 2 records per batch)

I have been trying to figure out for the whole day what is causing this but am unable to come to a conclusion. I wrote a lot of log statements but still am unable to figure out whats causing this error [UPDATE] I am getting a new error:

Updated Code:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Response Page</title> </head> <body> <div> <h1>Response</h1> <div> %%[ SET @rows = LookupRows("BU_PACKAGE_DETAILS_DE", "ACCOUNT_MID", "100029102") IF RowCount(@rows) > 0 THEN SET @clientId = Field(Row(@rows, 1), "CLIENT_ID") SET @clientSecret = Field(Row(@rows, 1), "CLIENT_SECRET") SET @accountId = Field(Row(@rows, 1), "ACCOUNT_MID") ELSE SET @clientId = "Not Found" SET @clientSecret = "Not Found" SET @accountId = "Not Found" ENDIF ]%% <script runat="server"> Platform.Load("Core", "1.1.1"); var clientId = Platform.Variable.GetValue("@clientId"); var clientSecret = Platform.Variable.GetValue("@clientSecret"); var accountId = Platform.Variable.GetValue("@accountId"); var batchSize = 2; // Process records in batches of 2 var batchNumber = 1; var continueProcessing = true; try { // Step 1: Retrieve Access Token Write("Step 1: Retrieving Access Token<br>"); var accessToken = getAccessToken(clientId, clientSecret, accountId); if (!accessToken) { throw new Error("Access token not retrieved"); } Write("Access Token: " + accessToken + "<br>"); while (continueProcessing) { Write("Entering while loop for batch number: " + batchNumber + "<br>"); try { // Step 2: Fetch records from Data Extension Write("Step 2: Fetching records from Data Extension<br>"); var records = Platform.Function.LookupOrderedRows("EJECTION_DE", batchSize, "RANK ASC", "PROCESSED_STATUS", "false"); Write("Records Retrieved: " + Platform.Function.Stringify(records) + "<br>"); if (records && records.length > 0) { var payload = []; for (var i = 0; i < records.length; i++) { var record = records[i]; var contactKey = record.PATIENT_COMPOSITE_KEY; var journeyDefinitionKey = record.JOURNEY_DEFINITION_KEY; Write("Processing Record " + i + ": ContactKey - " + contactKey + ", DefinitionKey - " + journeyDefinitionKey + "<br>"); payload.push({ "ContactKey": contactKey, "DefinitionKey": journeyDefinitionKey }); } Write("Batch Number: " + batchNumber + "<br>"); Write("Payload: " + Platform.Function.Stringify(payload) + "<br>"); var response = makeApiCall(accessToken, payload); Write("API Call Response Status Code: " + (response ? response.statusCode : "No Response") + "<br>"); Write("API Call Response: " + Platform.Function.Stringify(response) + "<br>"); if (response && (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 202)) { Write("API call to exit contacts from journey was successful.<br>"); for (var j = 0; j < records.length; j++) { var updateRecord = records[j]; Write("Updating Record " + j + ": Rank - " + updateRecord.RANK + "<br>"); var updateStatus = Platform.Function.UpdateData("EJECTION_DE", ["RANK"], [updateRecord.RANK], ["PROCESSED_STATUS", "TEST_RESPONSE_STATUS", "TEST_BATCH_NUMBER"], ["true", response.statusCode, batchNumber]); Write("Update Status: " + updateStatus + "<br>"); } } else { Write("Failed to exit contacts from journey. Status Code: " + (response ? response.statusCode : "No Response") + "<br>"); } batchNumber++; } else { continueProcessing = false; Write("No more records to process.<br>"); } } catch (fetchError) { Write("Error Fetching Records: " + fetchError.message + "<br>"); Write("Stack Trace: " + fetchError.stack + "<br>"); continueProcessing = false; } } } catch (ex) { Write("Error: " + ex.message + "<br>"); Write("Stack Trace: " + ex.stack + "<br>"); } function getAccessToken(clientId, clientSecret, accountId) { try { var authEndpoint = "https://REDACTED.auth.marketingcloudapis.com/v2/token"; var authPayload = Platform.Function.Stringify({ grant_type: "client_credentials", client_id: clientId, client_secret: clientSecret, account_id: accountId }); var authResponse = HTTP.Post(authEndpoint, "application/json", authPayload); Write("Auth Response: " + authResponse.StatusCode + "<br>"); if (authResponse.StatusCode === 200) { var authData = Platform.Function.ParseJSON(authResponse.Response[0]); return authData.access_token; } else { throw new Error("Failed to retrieve access token. Status Code: " + authResponse.StatusCode); } } catch (e) { Write("Error in getAccessToken: " + e.message + "<br>"); Write("Stack Trace: " + e.stack + "<br>"); return null; } } function makeApiCall(accessToken, payload) { try { var endpointUrl = "https://REDACTED.rest.marketingcloudapis.com/interaction/v1/interactions/contactexit"; var req = new Script.Util.HttpRequest(endpointUrl); req.retries = 3; req.continueOnError = true; req.setHeader("Authorization", "Bearer " + accessToken); req.method = "POST"; req.contentType = "application/json; charset=utf-8"; req.postData = Platform.Function.Stringify(payload); Write("Sending API Request...<br>"); var response = req.send(); if (response) { Write("API Response Status Code: " + response.statusCode + "<br>"); Write("API Response Content: " + response.content + "<br>"); } else { Write("No response from API call.<br>"); } if (response.contentType === "application/json") { return Platform.Function.ParseJSON(response.content); } else { return response.content; } } catch (e) { Write("Error in makeApiCall: " + e.message + "<br>"); Write("Stack Trace: " + e.stack + "<br>"); return null; } } </script> </div> </div> </body> </html> 

UPDATED ERROR OUTPUT:

Step 1: Retrieving Access Token Auth Response: 200 Access Token: REDACTED Step 2: Fetching records from Data Extension Records Retrieved: [{"_CustomObjectKey":114,"RANK":1",CreatedDate":"2024-06-12T05:20:20.600","TEST_RESPONSE_STATUS":"","TEST_BATCH_NUMBER":""}, {"SIMILARLY SECOND RECORD}] Error Fetching Records: Use of Common Language Runtime (CLR) is not allowed Stack Trace: undefined 

ARCHIVED OLD code:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Response Page</title> </head> <body> Response <div class="layout layout-canvas-g"> <div class="section"> <div class="columns col1"> <div data-type="slot" data-key="col1"> %%[ SET @rows = LookupRows("BU_PACKAGE_DETAILS_DE", "ACCOUNT_MID", "100029102") IF RowCount(@rows) > 0 THEN SET @clientId = Field(Row(@rows, 1), "CLIENT_ID") SET @clientSecret = Field(Row(@rows, 1), "CLIENT_SECRET") SET @accountId = Field(Row(@rows, 1), "ACCOUNT_MID") ELSE SET @clientId = "Not Found" SET @clientSecret = "Not Found" SET @accountId = "Not Found" ENDIF ]%% <script runat="server"> Platform.Load("Core", "1.1.1"); var clientId = Platform.Variable.GetValue("@clientId"); var clientSecret = Platform.Variable.GetValue("@clientSecret"); var accountId = Platform.Variable.GetValue("@accountId"); var batchSize = 2; // Process records in batches of 2 var batchNumber = 1; var continueProcessing = true; try { // Step 1: Retrieve Access Token Write("Step 1: Retrieving Access Token<br>"); var accessToken = getAccessToken(clientId, clientSecret, accountId); if (!accessToken) { throw new Error("Access token not retrieved"); } Write("Access Token: " + accessToken + "<br>"); while (continueProcessing) { // Step 2: Fetch records from Data Extension Write("Step 2: Fetching records from Data Extension<br>"); try { var records = Platform.Function.LookupOrderedRows("EJECTION_DE", batchSize, "RANK ASC", "PROCESSED_STATUS", "false"); Write("Records Retrieved: " + Platform.Function.Stringify(records) + "<br>"); if (Platform.Function.RowCount(records) > 0) { var payload = []; for (var i = 0; i < records.length; i++) { var record = records[i]; var contactKey = record.PATIENT_COMPOSITE_KEY; var journeyDefinitionKey = record.JOURNEY_DEFINITION_KEY; payload.push({ "ContactKey": contactKey, "DefinitionKey": journeyDefinitionKey }); } // Log the payload to ensure it is correctly formatted Write("Batch Number: " + batchNumber + "<br>"); Write("Payload: " + Platform.Function.Stringify(payload) + "<br>"); var response = makeApiCall(accessToken, payload); Write("API Call Response: " + Platform.Function.Stringify(response) + "<br>"); if (response && (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 202)) { Write("API call to exit contacts from journey was successful.<br>"); for (var j = 0; j < records.length; j++) { var updateRecord = records[j]; Platform.Function.UpdateData("EJECTION_DE", ["RANK"], [updateRecord.RANK], ["PROCESSED_STATUS", "TEST_RESPONSE_STATUS", "TEST_BATCH_NUMBER"], ["true", response.statusCode, batchNumber]); } } else { Write("Failed to exit contacts from journey. Status Code: " + (response ? response.statusCode : "No Response") + "<br>"); } batchNumber++; } else { continueProcessing = false; Write("No more records to process.<br>"); } } catch (fetchError) { Write("Error Fetching Records: " + fetchError.message + "<br>"); Write("Stack Trace: " + fetchError.stack + "<br>"); continueProcessing = false; } } } catch (ex) { Write("Error: " + ex.message + "<br>"); Write("Stack Trace: " + ex.stack + "<br>"); } function getAccessToken(clientId, clientSecret, accountId) { try { var authEndpoint = "https://SFMC URL.auth.marketingcloudapis.com/v2/token"; var authPayload = Platform.Function.Stringify({ grant_type: "client_credentials", client_id: clientId, client_secret: clientSecret, account_id: accountId }); var authResponse = HTTP.Post(authEndpoint, "application/json", authPayload); Write("Auth Response: " + authResponse.StatusCode + "<br>"); if (authResponse.StatusCode === 200) { var authData = Platform.Function.ParseJSON(authResponse.Response[0]); return authData.access_token; } else { throw new Error("Failed to retrieve access token. Status Code: " + authResponse.StatusCode); } } catch (e) { Write("Error in getAccessToken: " + e.message + "<br>"); Write("Stack Trace: " + e.stack + "<br>"); return null; } } function makeApiCall(accessToken, payload) { try { var endpointUrl = "https://SFMC URL.rest.marketingcloudapis.com/interaction/v1/interactions/contactexit"; var req = new Script.Util.HttpRequest(endpointUrl); req.retries = 3; req.continueOnError = true; req.setHeader("Authorization", "Bearer " + accessToken); req.method = "POST"; req.contentType = "application/json; charset=utf-8"; req.postData = Platform.Function.Stringify(payload); var response = req.send(); if (response.contentType === "application/json") { return Platform.Function.ParseJSON(response.content); } else { return response.content; } } catch (e) { Write("Error in makeApiCall: " + e.message + "<br>"); Write("Stack Trace: " + e.stack + "<br>"); return null; } } </script> </div> </div> </div> </div> </body> </html> 

Here is the output response:

Step 1: Retrieving Access Token Auth Response: 200 Access Token: REDACTED Step 2: Fetching records from Data Extension Records Retrieved: [{"_CustomObjectKey":114,"RANK":1",CreatedDate":"2024-06-12T05:20:20.600","TEST_RESPONSE_STATUS":"","TEST_BATCH_NUMBER":""}, {"SIMILARLY SECOND RECORD}] Error Fetching Records: Unable to retrieve security descriptor for this frame. Stack Trace: undefined 

NOTE: The code seems to work when I hardcode one record for ejection

PLEASE HELP ME OUT!

1 Answer 1

0

From your error output, the error must be within while (continueProcessing) {

after this line (which logs)

Write("Records Retrieved: " + Platform.Function.Stringify(records) + "<br>"); 

and before this line (which does not log)

 Write("Batch Number: " + batchNumber + "<br>"); 

So really not a ton of code to go through. Just inch forward and use "Writes" at every step.

First measure: Add further defensive measures to your lookupOrderedRows - use this pattern;

if(dataRows && dataRows.length > 0) 

from here: https://developer.salesforce.com/docs/marketing/marketing-cloud/guide/ssjs_platformDataExtensionLookupOrderedRows.html

Currently you have this:

if (Platform.Function.RowCount(records) > 0) { 

And I have never seen this applied. I wouldnt trust it. Just use standard SSJS as seen above; If that doesnt change it, rinse and repeat, just add one line and a "Write" and you can isolate this down to the erroneous piece quickly.

5
  • Thanks for the insights, I tried and got a new error this time, it says: Error Fetching Records: Use of Common Language Runtime (CLR) is not allowed after the first batch of two records is passed Commented Jun 13, 2024 at 7:57
  • I have pasted the updated code and error output in the question description too, any help will be much appreciated, thanks! Commented Jun 13, 2024 at 8:12
  • I'm serious - plaster it with write(). You can isolate this way better than we can do from the outside. Just every second line, add a debug statement. You'll have it tracked down to the row in no time, and then it gets way easier. Commented Jun 13, 2024 at 10:47
  • I'm quite done with SSJS, is there a way I can pass the response from SSJS and Use AMPscript to update the Data Extension. I wrote a lot of write commands and realised the update function is failing for some reason : ( This is even after removing the batching logic Commented Jun 13, 2024 at 11:42
  • yes possible; but failing updates can come from the data / constraints in the target DE like Primary keys, required fields, max length. So reworking this to AMPscript probably wont do much except add more overhead. sfmarketing.cloud/2022/09/06/… Commented Jun 13, 2024 at 12:28

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.