symfony/translation · error · InvalidArgumentException

Unable to format message

Error message

Unable to format message (error #%s): 

What it means

Thrown by IntlFormatter::formatIntl when MessageFormatter::format() returns false, meaning ICU failed to format an otherwise-parsable pattern — typically because arguments passed don't match the pattern's expected types, or the locale/args combination is invalid at format time.

Solutions

  1. Read the appended $formatter->getErrorMessage() to see the specific ICU failure
  2. Pass correctly-typed arguments (int/float for number and plural args) to trans()
  3. Ensure every argument referenced by the ICU pattern is supplied
  4. Normalize your parameter keys (strip % / {} wrappers) before calling trans()

Example fix

// before
$translator->trans('apples.count', ['%count%' => '5'], 'messages', 'en');
// after
$translator->trans('apples.count', ['%count%' => 5], 'messages', 'en');
Defensive patterns

Strategy: try-catch

Validate before calling

foreach ($patternArgs as $arg) { if (!array_key_exists($arg, $params)) { throw new \InvalidArgumentException("Missing ICU argument: {$arg}"); } }

Type guard

function areIcuArgsWellTyped(array $params): bool { foreach ($params as $k => $v) { if (is_string($v) && is_numeric($v)) { return false; } } return true; }

Try / catch

try { $msg = $translator->trans($id, $params, $domain, $locale); } catch (InvalidArgumentException $e) { $this->logger->error('ICU format failure for '.$id.': '.$e->getMessage()); $msg = $this->simpleFallback($id, $params); }

Prevention

When it happens

Trigger: Calling trans() with parameters whose types don't satisfy the ICU pattern (e.g. passing a string where a number is required for a plural/select argument), or missing required arguments.

Common situations: Passing string numbers ('5') into {count, number}-style args; forgetting to pass the plural/select argument; parameters keyed with '%key'/'{key}' conventions being stripped and remapped leaving required args absent.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of symfony/translation@ae9e8a51bc (2026-09-15). Data as JSON: /api/errors/6a67d386f16bee2f. Report an issue: GitHub.

Appendix: source

Thrown at Formatter/IntlFormatter.php:52

            if (!$this->hasMessageFormatter ??= class_exists(\MessageFormatter::class)) {
                throw new LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.');
            }
            try {
                $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message);
            } catch (\IntlException $e) {
                throw new InvalidArgumentException(\sprintf('Invalid message format (error #%d): ', intl_get_error_code()).intl_get_error_message(), 0, $e);
            }
        }

        foreach ($parameters as $key => $value) {
            if (\in_array($key[0] ?? null, ['%', '{'], true)) {
                unset($parameters[$key]);
                $parameters[trim($key, '%{ }')] = $value;
            }
        }

        if (false === $message = $formatter->format($parameters)) {
            throw new InvalidArgumentException(\sprintf('Unable to format message (error #%s): ', $formatter->getErrorCode()).$formatter->getErrorMessage());
        }

        return $message;
    }
}

View on GitHub (pinned to ae9e8a51bc)