symfony/translation · error · InvalidResourceException

This is not a local file

Error message

This is not a local file "%s".

What it means

When the XLIFF resource is not an inline XML string, the loader checks stream_is_local(); remote streams (http://, ftp:// wrappers) are rejected with this InvalidResourceException because Symfony only loads translation files from local storage.

Solutions

  1. Download the XLIFF file to a local path first, then pass the local file path to load().
  2. Use a stream wrapper that is local (e.g. copy to a temp file with copy() or file_get_contents and pass the XML string directly).
  3. If passing XML content, ensure it is a string starting with '<?xml' so isXmlString() treats it as inline content.

Example fix

// before
$loader->load('https://cdn.example.com/messages.en.xlf', 'en');
// after
$xml = file_get_contents('https://cdn.example.com/messages.en.xlf');
$loader->load($xml, 'en');
Defensive patterns

Strategy: validation

Validate before calling

if (!is_string($resource) || !str_starts_with(ltrim($resource), '<?xml')) {
    if (!stream_is_local($resource)) {
        throw new \InvalidArgumentException('Resource must be a local file or an XML string.');
    }
}

Try / catch

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

Prevention

When it happens

Trigger: Passing a URL or remote stream wrapper path as $resource to XliffFileLoader::load(), e.g. 'https://example.com/messages.en.xlf'.

Common situations: Developers pointing the translation loader at a CDN or remote translation service URL; configs built dynamically where the resource comes from an env var containing a URL.

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/c40f164cb575d491. Report an issue: GitHub.

Appendix: source

Thrown at Loader/XliffFileLoader.php:40

use Symfony\Component\Translation\MessageCatalogueInterface;
use Symfony\Component\Translation\Util\XliffUtils;

/**
 * XliffFileLoader loads translations from XLIFF files.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class XliffFileLoader 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 Xliff format requires the Symfony Config component.');
        }

        if (!$this->isXmlString($resource)) {
            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));
            }

            if (!is_file($resource)) {
                throw new InvalidResourceException(\sprintf('This is neither a file nor an XLIFF string "%s".', $resource));
            }
        }

        try {
            if ($this->isXmlString($resource)) {
                $dom = XmlUtils::parse($resource);
            } else {
                $dom = XmlUtils::loadFile($resource);
            }
        } catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) {

View on GitHub (pinned to ae9e8a51bc)