symfony/translation · error · InvalidArgumentException
Invalid message format
Error message
Invalid message format (error #%d):
What it means
Thrown by IntlFormatter::formatIntl when `new \MessageFormatter($locale, $message)` throws IntlException, i.e. the ICU message pattern itself is syntactically invalid for the given locale. The error code/message from intl_get_error_* are embedded in the exception text.
Solutions
- Read the appended intl error message to locate the syntax problem in the message string
- Fix the ICU pattern (balance braces, correct `{arg, type, format}` syntax)
- Validate messages with `msgfmt_format_message()` or an ICU message linter before deploying
- Isolate the offending message by formatting it directly with new \MessageFormatter($locale, $message)
Example fix
// before
'items.count': 'You have {count, plural, one {# item} other' // missing closing brace
// after
'items.count': 'You have {count, plural, one {# item} other {# items}}' Defensive patterns
Strategy: try-catch
Validate before calling
$mf = msgfmt_create($locale, $message); if ($mf === false) { $err = intl_get_error_message(); // reject the message before runtime formatting } Type guard
function isValidIcuPattern(string $locale, string $message): bool { return false !== msgfmt_format_message($locale, $message, []); } Try / catch
try { $msg = $translator->trans($id, $params, $domain, $locale); } catch (InvalidArgumentException $e) { $this->logger->error('Bad ICU message '.$id.': '.$e->getMessage()); $msg = $id; } Prevention
- Validate ICU messages in CI with msgfmt or a lint script over translation files
- Follow strict `{arg, type, format}` syntax; watch for unbalanced braces and quotes
- Have translators test plural/select patterns with sample data before merging
When it happens
Trigger: Formatting a translation message with malformed ICU syntax — unbalanced braces, invalid selector/argument syntax, bad plural rules — via $translator->trans() on an intl-formatted message.
Common situations: Hand-written translation files with ICU syntax mistakes (missing closing '}', stray quotes); translation files edited by non-developers or machine-generated; copy-pasting ICU patterns between formats.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Unable to format message
- Cannot parse message translation: please install the "intl"…
- The Translator does not support the following options
- The file dumper needs a path option.
- Unable to create directory
AI-assisted analysis of symfony/translation@ae9e8a51bc (2026-09-15).
Data as JSON: /api/errors/12455b0cf2084c01.
Report an issue: GitHub.
Appendix: source
Thrown at Formatter/IntlFormatter.php:40
{
private bool $hasMessageFormatter;
private array $cache = [];
public function formatIntl(string $message, string $locale, array $parameters = []): string
{
// MessageFormatter constructor throws an exception if the message is empty
if ('' === $message) {
return '';
}
if (!$formatter = $this->cache[$locale][$message] ?? null) {
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)