symfony/translation · error · InvalidResourceException

Unable to load " ".

Error message

Unable to load "%s".

What it means

XmlUtils::loadFile() validates the QT TS file against its DTD/schema; failures surface as InvalidArgumentException which the loader re-wraps as InvalidResourceException('Unable to load "<file>".'). The XML is unreadable, malformed, or fails QT's DTD validation.

Solutions

  1. Validate the XML independently: `xmllint --noout file.ts` to see the precise parse error and line.
  2. Fix the malformed XML (unclosed tags, bad entities, encoding declaration).
  3. Confirm the file is actually a QT TS file, not another XML dialect renamed to .ts.
  4. Re-export the .ts from Qt Linguist / lupdate; if DTD fetching is blocked, allow local DTD access or use a fully offline-valid file.
  5. Check the previous exception in the chain (InvalidArgumentException) for the underlying libxml message.

Example fix

// before (messages.ts)
<?xml version="1.0"?>
<TS><context><name>app</name>  // unclosed tags
// after
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0"><context><name>app</name></context></TS>
Defensive patterns

Strategy: try-catch

Validate before calling

libxml_use_internal_errors(true);
$dom = new \DOMDocument();
if (!$dom->load($resource)) {
    $err = libxml_get_last_error();
    throw new \RuntimeException(sprintf('%s is not valid XML: %s at line %d', $resource, trim($err->message), $err->line));
}
libxml_clear_errors();

Try / catch

try {
    $catalogue = $loader->load($resource, $locale);
} catch (InvalidResourceException $e) {
    error_log('QT XML load failed: '.$e->getMessage().' previous: '.$e->getPrevious()?->getMessage());
    $catalogue = new MessageCatalogue($locale);
}

Prevention

When it happens

Trigger: load() on a .ts file that is not well-formed XML, has wrong encoding, is an HTML/other-XML file misnamed .ts, or violates the QT translation DTD — raised in testLoad paths.

Common situations: Truncated downloads of .ts files, editors saving with wrong encoding, manually edited XML introducing syntax errors, DTD unreachable when validating against remote entities in air-gapped environments.

Related errors


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

Appendix: source

Thrown at Loader/QtFileLoader.php:45

{
    public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue
    {
        if (!class_exists(XmlUtils::class)) {
            throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.');
        }

        if (!stream_is_local($resource)) {
            throw new InvalidResourceException(\sprintf('This is not a local file "%s".', $resource));
        }

        if (!file_exists($resource)) {
            throw new NotFoundResourceException(\sprintf('File "%s" not found.', $resource));
        }

        try {
            $dom = XmlUtils::loadFile($resource);
        } catch (\InvalidArgumentException $e) {
            throw new InvalidResourceException(\sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
        }

        $internalErrors = libxml_use_internal_errors(true);
        libxml_clear_errors();

        $xpath = new \DOMXPath($dom);
        $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');

        $catalogue = new MessageCatalogue($locale);
        if (1 == $nodes->length) {
            $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
            foreach ($translations as $translation) {
                $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;

                if ($translationValue) {
                    $catalogue->set(
                        (string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
                        $translationValue,

View on GitHub (pinned to ae9e8a51bc)