symfony/translation · error · InvalidResourceException

$rb->getErrorMessage()

Error message

$rb->getErrorMessage()

What it means

When the ResourceBundle loads but ICU reports a failure code (intl_is_failure), the loader wraps ICU's own error message and code in InvalidResourceException. This means the bundle exists but ICU rejected it — e.g. a format/version mismatch between the .res file and the ICU library.

Solutions

  1. Read the exception's ICU error code and look it up in ICU's UErrorCode documentation to identify the exact failure.
  2. Regenerate .res bundles with genrb from a matching (or older) ICU toolchain.
  3. Upgrade the PHP intl extension / ICU library to a version compatible with the bundle files.
  4. Re-download or re-deploy the resource files if they are truncated or corrupted.

Example fix

// before
cat = $loader->load('messages.res', 'fr'); // ICU failure code
// after
$rb = new \ResourceBundle('fr', 'messages.res');
if (intl_is_failure($rb->getErrorCode())) {
    throw new \RuntimeException('ICU error '.$rb->getErrorCode().': rebuild bundles with matching ICU');
}
Defensive patterns

Strategy: try-catch

Validate before calling

$rb = new \ResourceBundle($locale, $resource);
if (!$rb || intl_is_failure($rb->getErrorCode())) {
    throw new \RuntimeException('ICU error '.$rb->getErrorCode().': '.$rb->getErrorMessage());
}

Type guard

function icuBundleLoads(string $locale, string $resource): bool {
    $rb = @new \ResourceBundle($locale, $resource);
    return $rb !== null && !intl_is_failure($rb->getErrorCode());
}

Try / catch

try {
    $catalogue = $loader->load($resource, $locale);
} catch (InvalidResourceException $e) {
    error_log('ICU failure: '.$e->getMessage().' code '.$e->getCode());
    $catalogue = $fallbackLoader->load($fallbackResource, $locale);
}

Prevention

When it happens

Trigger: load() on a .res file whose internal ICU error code (UErrorCode) indicates failure — truncated or wrongly-versioned bundle data, mismatched ICU version that produced the file. Raised in testDatEnglishLoad/testDatFrenchLoad flows.

Common situations: .res files generated with a newer ICU than the runtime intl extension; partially uploaded/truncated resource files; bundles referencing fallback locales missing from the data.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at Loader/IcuResFileLoader.php:45

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

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

        try {
            $rb = new \ResourceBundle($locale, $resource);
        } catch (\Exception) {
            $rb = null;
        }

        if (!$rb) {
            throw new InvalidResourceException(\sprintf('Cannot load resource "%s".', $resource));
        } elseif (intl_is_failure($rb->getErrorCode())) {
            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
        }

        $messages = $this->flatten($rb);
        $catalogue = new MessageCatalogue($locale);
        $catalogue->add($messages, $domain);

        if (class_exists(DirectoryResource::class)) {
            $catalogue->addResource(new DirectoryResource($resource));
        }

        return $catalogue;
    }

    /**
     * Flattens an ResourceBundle.
     *
     * The scheme used is:
     *   key { key2 { key3 { "value" } } }

View on GitHub (pinned to ae9e8a51bc)