symfony/translation · error · RuntimeException

Unable to create directory

Error message

Unable to create directory "%s".

What it means

Thrown by FileDumper::dump when mkdir() fails to create the target directory (or its parents) for the translation output file. The call is silenced with @, so any failure (permissions, path exists as file, read-only FS) surfaces as this RuntimeException.

Solutions

  1. Check/fix filesystem permissions on the parent directory so the PHP process can create it
  2. Verify no file exists at the directory path; remove or rename it
  3. Ensure the 'path' option points to a writable, correct location
  4. If using containers, mount the translations directory as writable by the runtime user

Example fix

// before
$dumper->dump($catalogue, ['path' => '/var/www/app/translations']); // /var/www/app not writable
// after
$dumper->dump($catalogue, ['path' => $kernel->getCacheDir().'/translations']);
Defensive patterns

Strategy: try-catch

Validate before calling

$dir = $options['path']; if (!is_dir($dir) && !@mkdir($dir, 0777, true) && !is_dir($dir)) { throw new \RuntimeException("Cannot prepare output dir {$dir}: ".error_get_last()['message']); }

Type guard

function isWritableOutputDir(string $path): bool { return is_dir($path) && is_writable($path); }

Try / catch

try { $dumper->dump($catalogue, $options); } catch (RuntimeException $e) { if (str_starts_with($e->getMessage(), 'Unable to create directory')) { // check permissions / run as correct user } throw $e; }

Prevention

When it happens

Trigger: dump() computes $fullpath = options['path'] + relative path; the parent directory does not exist and `mkdir($dir, 0777, true)` returns false — e.g. parent dir not writable, path segment is an existing file, or open_basedir restrictions.

Common situations: Writing translations to a directory owned by another user in production; Docker containers running as non-root with read-only volumes; a file existing where a directory is expected; misconfigured path parameter.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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

Appendix: source

Thrown at Dumper/FileDumper.php:53

     */
    public function setRelativePathTemplate(string $relativePathTemplate): void
    {
        $this->relativePathTemplate = $relativePathTemplate;
    }

    public function dump(MessageCatalogue $messages, array $options = []): void
    {
        if (!\array_key_exists('path', $options)) {
            throw new InvalidArgumentException('The file dumper needs a path option.');
        }

        // save a file for each domain
        foreach ($messages->getDomains() as $domain) {
            $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale());
            if (!file_exists($fullpath)) {
                $directory = \dirname($fullpath);
                if (!file_exists($directory) && !@mkdir($directory, 0o777, true)) {
                    throw new RuntimeException(\sprintf('Unable to create directory "%s".', $directory));
                }
            }

            $intlDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX;
            $intlMessages = $messages->all($intlDomain);

            if ($intlMessages) {
                $intlPath = $options['path'].'/'.$this->getRelativePath($intlDomain, $messages->getLocale());
                file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options));

                $messages->replace([], $intlDomain);

                try {
                    if ($messages->all($domain)) {
                        file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options));
                    }
                    continue;
                } finally {

View on GitHub (pinned to ae9e8a51bc)