Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

"There are Illegal characters in the string"?

60 views
Skip to first unread message

MJ

unread,
Jan 30, 2025, 10:29:20 PMJan 30
to Google Ads Scripts Forum
Been trying a few different script methods to pull and create dynamic ad group name custom parameters to pass eventually into FinalURL Suffixes which works for another script for campaign names but any idea why although the log gives no errors and looks good, it's telling me for each record status "There are illegal characters in the string". Tried searching and using gemini but nothing so far has solved this.

Here's the script currently:

function main() {
  processAccount();
}

function processAccount() {
  // Process all campaign types (including Search, Display, NOShopping, Performance Max)
  var campaignIterator = AdsApp.campaigns().get();
  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    processAdGroups(campaign);
  }

 // Optional: Not-Applicable as we don't offer Shopping Campaigns
//  var shoppingCampaignIterator = AdsApp.shoppingCampaigns().get();
//  while (shoppingCampaignIterator.hasNext()) {
//    var campaign = shoppingCampaignIterator.next();
//    processAdGroups(campaign);
//  }

 // Optional: Not-Applicable PMAX campaigns 
//  var pmaxCampaignIterator = AdsApp.performanceMaxCampaigns().get();
//  while (pmaxCampaignIterator.hasNext()) {
//    var campaign = pmaxCampaignIterator.next();
//    processAssetGroups(campaign);
//  }

 // Optional: Attempt to process Demand Gen campaigns with error handling
//  try {
//    var demandGenCampaignIterator = AdsApp.demandGenCampaigns().get();
//    while (demandGenCampaignIterator.hasNext()) {
//      var campaign = demandGenCampaignIterator.next();
//      processAdGroups(campaign);
//    }
//  } catch (e) {
//    Logger.log('Demand Gen campaigns may not be supported in this account: ' + e.message);
//  }

}


function processAdGroups(campaign) {
  var adGroupIterator = campaign.adGroups().get();
  while (adGroupIterator.hasNext()) {
    var adGroup = adGroupIterator.next();
    var adGroupName = adGroup.getName(); // Get the original name

    // More aggressive cleaning: Remove anything that is NOT a letter, number, underscore, or hyphen.
    var cleanedAdGroupName = adGroupName.replace(/[^a-zA-Z0-9_\-]/g, '');

    try {
      var modifiedAdGroupName = encodeURIComponent(cleanedAdGroupName);

      // Truncate if necessary (but 250 chars is usually enough)
      if (modifiedAdGroupName.length > 250) {
        Logger.log('Warning: Encoded name for "' + adGroupName + '" truncated.');
        modifiedAdGroupName = modifiedAdGroupName.substring(0, 250);
      }

      var customParameters = adGroup.urls().getCustomParameters() || {};
      customParameters['ad_groupname'] = modifiedAdGroupName;
      adGroup.urls().setCustomParameters(customParameters);

      Logger.log('Updated ad group "' + adGroupName + '" (cleaned: "' + cleanedAdGroupName + '") with custom parameter ad_groupname=' + modifiedAdGroupName);

    } catch (e) {
      Logger.log('ERROR encoding ad group name "' + adGroupName + '": ' + e.message);
      // Optionally, you could skip this ad group or try a different cleaning strategy here.
    }
  }
}

Google Ads Scripts Forum Advisor

unread,
Jan 30, 2025, 11:17:23 PMJan 30
to adwords...@googlegroups.com

Hi,

Thank you for reaching out to the Google Ads Scripts support team.

In order to assist you further, kindly provide us with the following details:

  • Google Ads account ID/CID
  • Name of the affected script
You can share the requested details via Reply privately to the author option or a direct private reply to this email.
 
This message is in relation to case "ref:!00D1U01174p.!5004Q02vGzNm:ref" (ADR-00286520)

Thanks,
 
Google Logo Google Ads Scripts Team

Feedback
How was our support today?

rating1    rating2    rating3    rating4    rating5



Search Engine Wings

unread,
Jan 31, 2025, 3:08:07 AMJan 31
to Google Ads Scripts Forum on behalf of adsscripts
Can you please elaborate on the issue?


--
-- You received this message because you are subscribed to the Google Groups AdWords Scripts Forum group. Please do not reply to this email. To post to this group or unsubscribe please visit https://developers.google.com/adwords/scripts/community.
---
You received this message because you are subscribed to the Google Groups "Google Ads Scripts Forum" group.
To unsubscribe from this group and stop receiving emails from it, send an email to adwords-scrip...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/adwords-scripts/fo_en000000000000000000000000000000000000000000000SQXQK200fbQYk1vSQQCHWI8tXtcwUw%40sfdc.net.

MJ

unread,
Feb 2, 2025, 7:32:36 PMFeb 2
to Google Ads Scripts Forum
Hello,
Absolutely - the script appears to run without any logged errors when I run a "Preview" of the script and check the "Logs" tab. Everything looks fine there but under the main "Changes" tab, for each change description, it continues to state "Status" as "There are illegal characters in the string". See attached screenshot for reference. A similar script for setting a custom parameter for campaigns works without this "There are illegal characters in the string" status happening so very confusing since there doesn't appear to be any illegal characters... I've tried URI encoding, illegal character cleaning/replacement, truncation, etc.... Nothing seems to work for this to create a custom parameter on the Ad Group level for having the Ad group name available as a custom parameter called "ad_groupname" / {_ad_groupname}. 
Google Ads AdGroup Name Custom Parameter Script Issue.png


Regards,
MJ

MJ

unread,
Feb 2, 2025, 7:32:44 PMFeb 2
to Google Ads Scripts Forum
Here's some additional information to help try and clarify what I'm trying to do and what the issue is as mentioned in my previous replies regarding the Ad script that gets Ad Group Names then cleans/URI Encodes/truncates the Ad Group Names, then attempts to create and set a new custom parameter on the Ad Group-level placing the "cleaned" Ad Group Names as values on this new _adgroupname custom parameter. The Script is nearly identical to one I use for Campaign Names on the Campaign-level for a custom parameter for campaignnames but for some reason on the Ad Group-level, the Ad Script indicates a status for every change performed that says "There are illegal characters in the string" when there are clearly no invalid characters in the cleaned Ad Group names that are being applied as values to the new Ad Group custom parameter defined by the script... Here's more info in case it helps understand what I'm doing, how I'm currently doing things, what the end goal is and what's "broken' or not working correctly.

I am trying to use Ad Scripts to grab Ad Group names, turn them into a custom parameter for example "adgroupname"/{_adgroupname} then use this custom parameter within a separate Google Ads Script to "dynamically" populate to add this new adgroupname custom parameter to set FinalURL Suffixes on the Campaign-level. For example, using an Ad Script to create that custom parameter for adgroupname on all ad groups, then another script that assigns to set all FinalURL Suffixes on the Campaign-level with this new Ad Group-level custom parameter "adgroupname" inserted into the Final URL Suffix such as "utm_source=google&utm_medium=cpc&utm_campaign={_campaignname}_{_adgroupname}&utm_content={creative}&utm_term={keyword}_{matchtype} " . I have 3-4 scripts that do this but the main issue that's preventing it from working flawlessly is the script trying to create the custom parameter on the Ad Group-level, unfortunately, that script doesn't appear to work even though it doesn't give any errors but indicates "There are illegal characters in the string" as the status for all Changes logged in the "Changes" tab of the script window as seen in the screenshot I shared earlier.

I'm trying to somehow grab the ad group and campaign names so that when someone clicks an Ad, the finalURL/utm information "dynamically" populates with the ad group and campaign name information somewhere within a UTM property automatically. 

I have Google Ad Scripts to apply and set a FinalURL suffix on the campaign-level referencing custom parameters for "campaignname" (set by another Google Ad Script on Campaign-level), custom parameter "adgroupname" (which is the one I'm having issues with here that's set on the Ad Group-level). The end result with this would be 1-custom ad group-level custom parameter for the adgroup name, 1-custom campaign-level parameter for campaign name and then 1 regularly ran script that applies these custom parameters into a FinalURL Suffix on the Campaign-level for every campaign (Search & Display only).

Here are the 3 Google Ads Scripts I'm using:
Google Ads Script# 1) Grab Campaign Names, clean/URI Encode/truncate text, create custom parameter called "campaignname" for all campaigns and add the new cleaned campaign names into this parameter value (This applies custom parameter on Campaign-level)
Google Ads Script# 2) Grab Ad Group Names, clean/URI Encode/truncate text, create custom parameter called "adgroupname" for all ad groups and add the new cleaned ad group names into this parameter value (This applies custom parameter on Ad Group-level)
Google Ads Script# 3) Apply/set/update FinalURL suffix to all Campaigns (campaign-level) using the newly created custom parameters such as "utm_source=google&utm_medium=cpc&utm_campaign={_campaignname}_{_adgroupname}&utm_content={creative}&utm_term={keyword}_{matchtype}"

However, I haven't been able to get the adgroupname script to actually work due to a status indicating "There are illegal characters in the string" but the other Google Ad Scripts work perfectly. The log indicates no errors when debugging/previewing the Ad Group name custom parameter script but it doesn't apply even though it appears it should be and in the "Changes" log, it says "There are illegal characters in the string" for every line processed. Any ideas why this is happening and what I can do to resolve this so I can utilize and reference Ad Group Names "dynamically" from the Campaign-level FinalURL Suffix like I should be able to with the defined Ad Group-level custom parameter "adgroupname"?



Regards,
MJ





On Friday, January 31, 2025 at 2:08:07 AM UTC-6 Search Engine Wings wrote:

MJ

unread,
Feb 2, 2025, 7:32:57 PMFeb 2
to Google Ads Scripts Forum
Hello All,
UPDATE - Resolved: Following up on this as turns out I was able to identify what the culprit was leading to the status messages saying "There are illegal characters in the string". The issue was caused by attempting to define the custom parameter as "ad_groupname" which was then being referenced as "_ad_groupname". Apparently, you cannot use underscores or anything other than strict alphanumeric characters in naming custom parameters even though I thought I already tried that before, must not have. I simply changed the script definition for Ad Group-level custom parameter from "ad_groupname" to remove the underscore, defining it as "adgrpname" instead and everything worked as expected. Simple fix of course, staring at me the entire time but thank you for your help and time.

For future reference, if anyone is looking to use custom scripts to set Campaign-level FinalURL Suffixes so that the UTM data dynamically populates dynamic ad group names and campaign names automatically, below are the scripts I'm using (there are three separate scripts as I'm not adept enough to combine them further). I hope this helps others who I think are probably looking for something similar on here.

Script #1 - Dynamic Custom Parameter for Campaign Name(s) | Applies on Campaign-level
// This script sets the custom parameter campaign (_campaign) for all campaigns in the account.
// This parameter contains the campaign name transformed into a URL-compatible format and can be used in the tracking template or final URL suffix.
// Before applying: Ensure that this parameter is not used for another purpose to avoid overwriting important information.
// Also, check that there isn’t a campaign parameter with an empty value, as this can cause errors.
// [Schedule] The script should be scheduled to run regularly, e.g., every hour, to add parameters to new campaigns.
// It is also good to manually run it after adding new campaigns.
// [Warning] As of October 27, 2024: The script does not work for all campaign types, including Demand Gen and Video. These need to be updated manually using Google Ads Editor.
// Remember: Changing a campaign name will leave the old names in historical data in Analytics. Therefore, avoid changing campaign names.
// © ADEQUATE www.adequate.digital 2024
// More info: https://adequate.digital/en/utms-for-google-ads-campaigns/


function main() {
  processAccount();
}

function processAccount() {
  // Process Standard Campaigns (Search and Display)
 try {

  var campaignIterator = AdsApp.campaigns().get();
  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    processCampaign(campaign);
  }
   }
  catch (e) {
    Logger.log('Search and Display Campaigns encountered an issue: ' + e.message);
  }

// Optional: Process Shopping Campaigns - NOT APPLICABLE
// try {

//  var shoppingCampaignIterator = AdsApp.shoppingCampaigns().get();
//  while (shoppingCampaignIterator.hasNext()) {
//    var campaign = shoppingCampaignIterator.next();
//    processCampaign(campaign);
//  }
//}
//  catch (e) {
//    Logger.log('Shopping Campaigns encountered an issue: ' + e.message);
//  }

  // Process Performance Max Campaigns
  try {
    var pmaxCampaignIterator = AdsApp.performanceMaxCampaigns().get();
    while (pmaxCampaignIterator.hasNext()) {
    var campaign = pmaxCampaignIterator.next();
    processCampaign(campaign);
  }
  }
  catch (e) {
    Logger.log('PMAX Campaigns encountered an issue: ' + e.message);
  }

// Optional: Process Demand Gen campaigns with error handling - NOT APPLICABLE

//  try {
//    var demandGenCampaignIterator = AdsApp.demandGenCampaigns().get();
//    while (demandGenCampaignIterator.hasNext()) {
//      var campaign = demandGenCampaignIterator.next();
//      processCampaign(campaign);

//    }
//  } catch (e) {
//    Logger.log('Demand Gen campaigns may not be supported in this account: ' + e.message);
//  }

}

function processCampaign(campaign) {
  var campaignName = campaign.getName(); // Get the original campaign name

  // Standardization: Remove anything that is NOT a letter, number, underscore, or hyphen.
    var cleanedCampaignName = campaignName.replace(/[^a-zA-Z0-9_\-]/g, '');
 
  // Try Percent-encode the campaign name
  try{
  var modifiedCampaignName = encodeURIComponent(cleanedCampaignName);

  // Truncate if necessary to 250 characters
  if (modifiedCampaignName.length > 250) {
    Logger.log('Warning: The encoded campaign name for "' + campaignName + '" exceeds 250 characters and will be truncated.');
    modifiedCampaignName = modifiedCampaignName.substring(0, 250);
  }

  // Get existing custom parameters at the campaign level
  var customParameters = campaign.urls().getCustomParameters() || {};

  // Update or add the 'campaign' parameter
  customParameters['campaign'] = modifiedCampaignName;

  // Set the updated custom parameters back to the campaign
  campaign.urls().setCustomParameters(customParameters);

  Logger.log('Updated campaign "' + campaignName + '" (cleaned: "' + cleanedCampaignName + '")  with custom parameter campaign=' + modifiedCampaignName);
}
  catch (e) {
      Logger.log('ERROR encoding campaign name "' + campaignName + '": ' + e.message);
      // Optionally, you could skip this campaign or try a different cleaning strategy here.
    }
  }



Script #2 - Dynamic Custom Parameter for Ad Group Name(s) | Applies on Ad Group-level
// This script sets the custom parameter adgrpname (_adgrpname) for all ad groups in the account.
// This parameter contains the ad group name transformed into a URL-compatible format removing special characters and truncating for brevity
// and can be used in the tracking template or final URL suffix.

// Before applying: Ensure that this parameter is not used for another purpose to avoid
// overwriting important information. Also, check that there isn’t an ad group parameter
// with an empty value, as this can cause errors.

// [Schedule] The script should be scheduled to run regularly, e.g., every hour, to add
// parameters to new ad groups. It is also good to manually run it after adding new ad groups.

// [Warning] As of October 27, 2024: The script does not work for all campaign types,
// including Demand Gen and Video. These need to be updated manually using Google Ads Editor.

// Remember: Changing an ad group name will leave the old names in historical data in Analytics.
// Therefore, avoid changing ad group names.


function main() {
  processAccount();
}

function processAccount() {
  // Process all campaign types (including Search, Display, Shopping, Performance Max)
  try {

  var campaignIterator = AdsApp.campaigns().get();
  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    processAdGroups(campaign);
  }
 } catch (e) {
    Logger.log('Search Campaigns encountered an issue: ' + e.message);
  }
 // Optional: Not-Applicable Shopping Campaigns
//  var shoppingCampaignIterator = AdsApp.shoppingCampaigns().get();
//  while (shoppingCampaignIterator.hasNext()) {
//    var campaign = shoppingCampaignIterator.next();
//    processAdGroups(campaign);
//  }

 // Optional: Not-Applicable
//  var pmaxCampaignIterator = AdsApp.performanceMaxCampaigns().get();
//  while (pmaxCampaignIterator.hasNext()) {
//    var campaign = pmaxCampaignIterator.next();
//    processAssetGroups(campaign);
//  }

 // Optional: Attempt to process Demand Gen campaigns with error handling
//  try {
//    var demandGenCampaignIterator = AdsApp.demandGenCampaigns().get();
//    while (demandGenCampaignIterator.hasNext()) {
//      var campaign = demandGenCampaignIterator.next();
//      processAdGroups(campaign);
//    }
//  } catch (e) {
//    Logger.log('Demand Gen campaigns may not be supported in this account: ' + e.message);
//  }

}

function processAdGroups(campaign) {
  var adGroupIterator = campaign.adGroups().get();
  while (adGroupIterator.hasNext()) {
    var adGroup = adGroupIterator.next();
    var adGroupName = adGroup.getName(); // Get the original name

    // More aggressive cleaning: Remove anything that is NOT a letter, number, underscore, or hyphen and enforcing all lowercase.
    var cleanedAdGroupName = adGroupName.replace(/[^a-zA-Z0-9_\-]/g, '').toLowerCase();


    try {
      var modifiedAdGroupName = encodeURIComponent(cleanedAdGroupName);

      // Truncate if necessary (but 250 chars is usually enough)
      if (modifiedAdGroupName.length > 250) {
        Logger.log('Warning: Encoded name for "' + adGroupName + '" truncated.');
        modifiedAdGroupName = modifiedAdGroupName.substring(0, 250);
      }

      var customParameters = adGroup.urls().getCustomParameters() || {};
      customParameters['adgrpname'] = modifiedAdGroupName;
      adGroup.urls().setCustomParameters(customParameters);

      Logger.log('Updated ad group "' + adGroupName + '" (cleaned: "' + cleanedAdGroupName + '") with custom parameter adgrpname=' + modifiedAdGroupName);


    } catch (e) {
      Logger.log('ERROR encoding ad group name "' + adGroupName + '": ' + e.message);
      // Optionally, you could skip this ad group or try a different cleaning strategy here.
    }
  }
}

Script #3 - Dynamic UTM Deployment to include and apply Campaign Name(s) and Ad Group Name(s) via FinalURL Suffix on all Search/Display Campaigns Only
// This script sets the following URL suffix for all campaigns in the account dynamically populating both campaign and adgroup names under UTM_Campaign:
// utm_source=google&utm_medium=cpc&utm_campaign={_campaign}_{_adgrpname}&utm_id={campaignid}&utm_term={keyword}_{matchtype}&utm_content={creative}&utm_source_platform=google+ads
// (If you want a different suffix, modify the script below.)
// Note: For this to work, custom campaign parameters (campaign) and custom ad group parameters (adgrpname) need to be set separately using a different script or manually.
// Warning: Before applying this, remove similar tracking templates if you are using them ({lpurl}?utm_source=google&utm_medium...) to avoid tag duplication.
// Check this for Account and Ad Group Settings as well!
// For account-level settings: Also set this suffix in the Google Ads panel (currently the only option).
// Scheduling: The script should be scheduled to run regularly, e.g., every hour, to add parameters to new campaigns.
// You can also manually run it after adding new campaigns.
// Warning (as of October 27, 2024): The script does not work for certain types of campaigns like Demand Gen and Video campaigns. These need to be updated manually using Google Ads Editor.
// Original Credit to © ADEQUATE www.adequate.digital 2024
// NOTE: This script is part of a batch of 3-Scripts necessary for successful deployment as-is.
// More info: https://adequate.digital/en/utms-for-google-ads-campaigns/

function main() {
  // Process standard campaigns (Search and Display) with error handling
  try{

  var campaignIterator = AdsApp.campaigns().get();
  while (campaignIterator.hasNext()) {
    var campaign = campaignIterator.next();
    processCampaign(campaign);
  }
    }
  catch (e) {
    Logger.log('Search Campaigns encountered an issue: ' + e.message);
  }

 //Optional: Process Shopping campaigns with error handling

// var shoppingCampaignIterator = AdsApp.shoppingCampaigns().get();
// while (shoppingCampaignIterator.hasNext()) {
//   var campaign = shoppingCampaignIterator.next();
//    processCampaign(campaign);
//  }

  //Optional: Attempt to process Performance Max campaigns with error handling
  try {
    var pmaxCampaignIterator = AdsApp.performanceMaxCampaigns().get();
    while (pmaxCampaignIterator.hasNext()) {
    var campaign = pmaxCampaignIterator.next();
    processCampaign(campaign);
  }
    }
  catch (e) {
    Logger.log('PMAX Campaigns encountered an issue: ' + e.message);

  }

  // Optional: Attempt to process Demand Gen campaigns with error handling
//  try {
//    var demandGenCampaignIterator = AdsApp.demandGenCampaigns().get();
//    while (demandGenCampaignIterator.hasNext()) {
//      var campaign = demandGenCampaignIterator.next();
//      processCampaign(campaign);

//    }
//  } catch (e) {
//    Logger.log('Demand Gen campaigns may not be supported in this account: ' + e.message);
//  }
}

function processCampaign(campaign) {
  // Set the Final URL Suffix at the Campaign-level using Campaign Custom Parameter for Campaign Names and Ad Group Custom Parameter for Ad Group Names along with Ad Search Keyword & Matchtype
  var campaignFinalUrlSuffix = "utm_source=google&utm_medium=cpc&utm_campaign={_campaign}_{_adgrpname}&utm_id={campaignid}&utm_term={keyword}_{matchtype}&utm_content={creative}&utm_source_platform=google+ads";
  campaign.urls().setFinalUrlSuffix(campaignFinalUrlSuffix);

  Logger.log('Updated campaign "' + campaign.getName() + '" with campaign-level Final URL Suffix.');
}


Regards,
MJ





On Friday, January 31, 2025 at 2:08:07 AM UTC-6 Search Engine Wings wrote:
Reply all
Reply to author
Forward
0 new messages