GoogleChrome/lighthouse · error

must use same format and formatType for a given name. Invali

Error message

must use same format and formatType for a given name. Invalid for: ${rawName}

What it means

Thrown during CTC conversion when the same placeholder rawName is used more than once in a message but with a different `format` or `formatType` (e.g. `{timeInMs, number, milliseconds}` vs `{timeInMs, number, seconds}`). The pipeline requires that a given placeholder name always maps to the same custom format so the generated CUSTOM_ICU_N placeholder is unambiguous.

Source

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

    icu.message += preambleText;

    if (!rawName || !format || !formatType) continue;
    // Check that custom-formatted ICU not using non-supported format ex:
    // * using a second arg anything other than "number"
    // * using a third arg that is not millis, secs, bytes, %, or extended %
    if (!format.match(/^number$/)) {
      throw Error(`Unsupported custom-formatted ICU format var "${format}" in message "${icu.message}"`);
    }
    if (!formatType.match(/milliseconds|seconds|bytes|percent|extendedPercent/)) {
      throw Error(`Unsupported custom-formatted ICU type var "${formatType}" in message "${icu.message}"`);
    }

    let index;
    const previousRawName = rawNameCache.get(rawName);
    if (previousRawName) {
      const [prevFormat, prevFormatType, prevIndex] = previousRawName;
      if (prevFormat !== format || prevFormatType !== formatType) {
        throw new Error(`must use same format and formatType for a given name. Invalid for: ${rawName}`);
      }

      index = prevIndex;
    } else {
      index = idx++;
      rawNameCache.set(rawName, [format, formatType, index]);
    }

    // Append ICU replacements if there are any.
    const placeholderName = `CUSTOM_ICU_${index}`;
    icu.message += `$${placeholderName}$`;
    let example;

    // Make some good examples.
    switch (formatType) {
      case 'seconds':
        example = '2.4';
        break;

View on GitHub (pinned to 9515cd4e58)

Solutions

  1. Rename one of the conflicting placeholders so each name maps to exactly one format/formatType pair.
  2. Align both usages to the same format and formatType.
  3. Search the message string for the rawName and reconcile all occurrences.

Example fix

// before
message: '{timeInMs, number, milliseconds} ms ({timeInMs, number, seconds})'
// after
message: '{timeInMs, number, milliseconds} ms ({timeInSec, number, seconds})'
Defensive patterns

Strategy: validation

Validate before calling

function checkPlaceholderFormatConsistency(message) {
  const re = /\{(?<name>\w+),\s*number,\s*(?<format>\w+)\}/g;
  const seen = new Map();
  let m;
  while ((m = re.exec(message))) {
    if (seen.has(m.groups.name) && seen.get(m.groups.name) !== m.groups.format) {
      return false;
    }
    seen.set(m.groups.name, m.groups.format);
  }
  return true;
}

Prevention

When it happens

Trigger: Using the same ICU placeholder name with two different custom format annotations within the same message or across messages that share a name in the same conversion pass.

Common situations: Copy-pasting a placeholder for a different unit (bytes vs milliseconds) and forgetting to rename; refactoring that changes a format type in one place but not another.

Related errors


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