symfony/translation · error · InvalidResourceException

This is not a local file

Error message

This is not a local file "%s".

What it means

QtFileLoader only reads local filesystem resources; stream_is_local() rejects remote URLs/wrappers such as http:// or ftp://, since XmlUtils::loadFile requires a local path. It throws InvalidResourceException with the offending resource path.

Solutions

  1. Download the .ts file to a local temp path (file_get_contents/curl) and pass the local path to load().
  2. Mount or sync remote translation files locally as part of deployment.
  3. If resources truly must be remote, write a custom loader that streams content and parses via a DOMDocument loaded from a string.
  4. Fix config that interpolates a base URL into the resource path.

Example fix

// before
$loader->load('https://cdn.example.com/messages.fr.ts', 'fr');
// after
$tmp = sys_get_temp_dir().'/messages.fr.ts';
file_put_contents($tmp, file_get_contents('https://cdn.example.com/messages.fr.ts'));
$loader->load($tmp, 'fr');
Defensive patterns

Strategy: validation

Validate before calling

if (!stream_is_local($resource)) {
    throw new \RuntimeException(sprintf('Translation resource must be local, got: %s', $resource));
}

Type guard

function isLocalFilePath(mixed $resource): bool {
    return is_string($resource) && stream_is_local($resource) && !preg_match('#^[a-z][a-z0-9+.-]*://#i', $resource);
}

Try / catch

try {
    $catalogue = $loader->load($resource, $locale);
} catch (InvalidResourceException $e) {
    if (str_contains($e->getMessage(), 'not a local file')) {
        $local = downloadToTemp($resource);
        $catalogue = $loader->load($local, $locale);
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: load() called with a non-local stream target, e.g. 'https://example.com/messages.ts', 'ftp://...', or a custom stream wrapper — raised in testLoad paths.

Common situations: Configuring the translation resource path from a CDN/remote origin, passing a URL built from environment config, trying to load bundles straight from object storage.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at Loader/QtFileLoader.php:35

use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Translation\Exception\RuntimeException;
use Symfony\Component\Translation\MessageCatalogue;

/**
 * QtFileLoader loads translations from QT Translations XML files.
 *
 * @author Benjamin Eberlei <kontakt@beberlei.de>
 */
class QtFileLoader implements LoaderInterface
{
    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.'"]');

View on GitHub (pinned to ae9e8a51bc)