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
- Read the appended $formatter->getErrorMessage() to see the specific ICU failure
- Pass correctly-typed arguments (int/float for number and plural args) to trans()
- Ensure every argument referenced by the ICU pattern is supplied
- 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
- Pass native int/float values for numeric ICU arguments, never numeric strings
- Always supply every argument referenced by plural/select patterns
- Document the parameter contract per translation key so callers pass correct types
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
- Invalid message format
- 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/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)