composer/composer · error · UnexpectedValueException

does not exist and could not be created.

Error message

${dir} does not exist and could not be created.

What it means

JsonFile::write() throws an UnexpectedValueException when the parent directory does not exist and @mkdir($dir, 0777, true) returns false. mkdir fails silently here (error suppressed with @), so the message reports only that creation failed; the suppressed error usually indicates a permissions issue or a path collision.

Solutions

  1. Fix ownership/permissions on the parent directory so the PHP user can write and create subdirs: `chown -R <user>:<group> <parent>` and `chmod -R u+w <parent>`.
  2. Ensure you are running composer as the directory owner (not root) or with matching UID in Docker.
  3. Remove any file that conflicts with a needed directory segment.
  4. Check open_basedir / disable functions if on a restricted host.

Example fix

# before (docker, wrong UID)
$ composer install
> .../vendor does not exist and could not be created.

# after
$ chown -R $USER:$USER .
$ chmod -R u+w .
$ composer install
Defensive patterns

Strategy: validation

Validate before calling

$dir = dirname($path);
if (!is_dir($dir)) {
    if (file_exists($dir)) {
        throw new \RuntimeException("$dir is a file, cannot create directory");
    }
    if (!@mkdir($dir, 0777, true)) {
        throw new \RuntimeException("Cannot create $dir: " . (error_get_last()['message'] ?? 'permission denied'));
    }
}

Type guard

function dirIsCreatable(string $dir): bool {
    if (is_dir($dir)) return true;
    if (file_exists($dir)) return false;
    return is_writable(dirname($dir));
}

Try / catch

try {
    $jsonFile->write($data);
} catch (\UnexpectedValueException $e) {
    if (str_ends_with($e->getMessage(), ' does not exist and could not be created.')) {
        // fix ownership/permissions or open_basedir, then retry
        throw new \RuntimeException('Cannot create dir for ' . $path . ': ' . $e->getMessage(), 0, $e);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling write() where dirname($path) is missing and cannot be created — e.g. a parent segment lacks write permission, a component of the path is an existing file, or open_basedir restricts the location.

Common situations: vendor/ or its parent not writable by the PHP user (common on shared hosts, Docker with wrong UID, CI running as non-owner); running composer as root with a HOME that maps to an unreadable path; open_basedir restriction; a file blocking the directory creation.

Related errors


AI-assisted analysis of composer/composer@c435d285c9 (2026-08-07). Data as JSON: /api/errors/6b38ce2f9a6c3173. Report an issue: GitHub.

Appendix: source

Thrown at src/Composer/Json/JsonFile.php:154

     * @return void
     */
    public function write(array $hash, int $options = JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
    {
        if ($this->path === 'php://memory') {
            file_put_contents($this->path, static::encode($hash, $options, $this->indent));

            return;
        }

        $dir = dirname($this->path);
        if (!is_dir($dir)) {
            if (file_exists($dir)) {
                throw new \UnexpectedValueException(
                    realpath($dir).' exists and is not a directory.'
                );
            }
            if (!@mkdir($dir, 0777, true)) {
                throw new \UnexpectedValueException(
                    $dir.' does not exist and could not be created.'
                );
            }
        }

        $retries = 3;
        while ($retries--) {
            try {
                $this->filePutContentsIfModified($this->path, static::encode($hash, $options, $this->indent). ($options & JSON_PRETTY_PRINT ? "\n" : ''));
                break;
            } catch (\Exception $e) {
                if ($retries > 0) {
                    usleep(500000);
                    continue;
                }

                throw $e;
            }

View on GitHub (pinned to c435d285c9)