GoogleChrome/lighthouse · error

Ctc messages cannot contain double dollar: ${icu.message}

Error message

Ctc messages cannot contain double dollar: ${icu.message}

What it means

Thrown by _ctcValidityChecks() when a converted CTC (CTC = internal canonical format) message contains `$$` (double dollar). In CTC format, placeholders are delimited by `$...$`, so an empty `$...$` pair (i.e. `$$`) is always invalid — it implies a placeholder with no name, which cannot be resolved or translated.

Source

Thrown at core/scripts/i18n/collect-strings.js:436

    };
  }
  icu.message = tempMessage;
}

/**
 * Do some basic checks on a ctc object to confirm that it is valid. Future
 * ctc regression catching should go here.
 *
 * @param {IncrementalCtc} icu the ctc output message to verify
 */
function _ctcValidityChecks(icu) {
  // '$$' i.e. "Double Dollar" is always invalid in ctc.
  const regex = /\$([^$]*?)\$/g;
  const matches = regex.exec(icu.message);
  if (Array.isArray(matches)) {
    matches.forEach(function(value) {
      if (!value) {
        throw new Error(`Ctc messages cannot contain double dollar: ${icu.message}`);
      }
    });
  }
}

/**
 * Take a series of messages and apply ĥât̂ markers to the translatable portions
 * of the text.  Used to generate `en-XL` locale to debug i18n strings. This is
 * done while messages are in `ctc` format, and therefore modifies only the
 * messages themselves while leaving placeholders untouched.
 *
 * @param {Record<string, CtcMessage>} messages
 * @return {Record<string, CtcMessage>}
 */
function createPsuedoLocaleStrings(messages) {
  /** @type {Record<string, CtcMessage>} */
  const psuedoLocalizedStrings = {};
  for (const [key, ctc] of Object.entries(messages)) {

View on GitHub (pinned to 9515cd4e58)

Solutions

  1. Inspect the source LHL message that produced the CTC output and ensure all ICU placeholders are well-formed and non-empty.
  2. If this appears after a code change to the conversion logic, debug convertMessageToCtc for the specific message.
  3. Ensure no literal `$` characters exist in source messages, or escape them per the project's convention.
Defensive patterns

Strategy: validation

Validate before calling

function ctcHasDoubleDollar(message) {
  const re = /\$([^$]*?)\$/g;
  let m;
  while ((m = re.exec(message))) {
    if (!m[1]) return true; // empty capture means $$ found
  }
  return false;
}

Prevention

When it happens

Trigger: A converted message ends up with adjacent `$` delimiters producing `$$`, usually from a conversion bug or a source message that had degenerate placeholder usage.

Common situations: Bugs in convertMessageToCtc; source LHL messages with empty or adjacent ICU placeholders that collapse to `$$` after conversion; manual edits to generated CTC output.

Related errors


AI-assisted analysis of GoogleChrome/lighthouse@9515cd4e58 (2026-08-13). Data as JSON: /api/errors/a9d51f0c64812830. Report an issue: GitHub.