composer/composer · error · TransportException
The "{url}" file could not be written to {destination}: {err
Error message
The "{url}" file could not be written to {destination}: {error} What it means
TransportException thrown in CurlDownloader::initDownload() when fopen() of the response body destination fails. The destination is either the user-supplied $copyTo.'~' (when saving a file) or a php://temp stream; the captured PHP fopen error message is appended so the root cause (disk full, permissions, invalid path) is visible.
Source
Thrown at src/Composer/Util/Http/CurlDownloader.php:216
if ($copyTo !== null) {
$bodyTarget = $copyTo.'~';
} else {
$bodyTarget = 'php://temp/maxmemory:524288';
}
$errorMessage = '';
set_error_handler(static function (int $code, string $msg) use (&$errorMessage): bool {
if ($errorMessage) {
$errorMessage .= "\n";
}
$errorMessage .= Preg::replace('{^fopen\(.*?\): }', '', $msg);
return true;
});
$bodyHandle = fopen($bodyTarget, 'w+b');
restore_error_handler();
if (false === $bodyHandle) {
throw new TransportException('The "'.Url::sanitize($url).'" file could not be written to '.($copyTo ?? 'a temporary file').': '.$errorMessage);
}
curl_setopt($curlHandle, CURLOPT_URL, $url);
curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curlHandle, CURLOPT_TIMEOUT, max((int) ini_get("default_socket_timeout"), 300));
curl_setopt($curlHandle, CURLOPT_WRITEHEADER, $headerHandle);
curl_setopt($curlHandle, CURLOPT_FILE, $bodyHandle);
curl_setopt($curlHandle, CURLOPT_ENCODING, ""); // let cURL set the Accept-Encoding header to what it supports
curl_setopt($curlHandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
if ($attributes['ipResolve'] === 4) {
curl_setopt($curlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
} elseif ($attributes['ipResolve'] === 6) {
curl_setopt($curlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
}
if ($attributes['retries'] > 0) {View on GitHub (pinned to 6ffc117740)
Solutions
- Verify write permission and free space on the target directory and COMPOSER_HOME: `df -h` and `touch <dir>/test` then remove it.
- Set COMPOSER_HOME to a writable location: `export COMPOSER_HOME=/tmp/composer` (or a persistent writable dir).
- Fix ownership/permissions: `chown -R $USER <dir>` / `chmod -R u+w <dir>`.
- Clear the Composer cache (`composer clear-cache`) in case the cache dir is the culprit.
Example fix
// before: target dir not writable by current user // composer install # => could not be written to /var/www/vendor/...~: Permission denied // after // sudo chown -R $USER:$USER /var/www // composer install
Defensive patterns
Strategy: validation
Validate before calling
$dest = $copyTo ?? sys_get_temp_dir();
if (!is_writable(dirname($dest))) {
throw new \RuntimeException('Destination dir not writable: '.dirname($dest));
} Type guard
function isWritableTarget(?string $copyTo): bool {
return $copyTo === null || is_writable(dirname($copyTo));
} Try / catch
try {
$downloader->get($url, [], $copyTo);
} catch (\Composer\Downloader\TransportException $e) {
if (str_contains($e->getMessage(), 'could not be written to')) {
// fix perms / free disk / set COMPOSER_HOME, then retry
}
throw $e;
} Prevention
- Ensure COMPOSER_HOME and the cache/vendor dirs are writable by the Composer user.
- Monitor free disk space in CI before running installs.
- Use absolute paths for copyTo to avoid cwd ambiguity.
When it happens
Trigger: initDownload() reaches line 213 fopen($bodyTarget,'w+b') and it returns false; the set_error_handler captured $errorMessage. Happens when $copyTo points at a non-writable/unreachable directory, disk is full, or the temp stream cannot be allocated.
Common situations: COMPOSER_HOME or cache dir on a read-only filesystem; vendor dir owned by another user; disk/quota exhausted; very long path on Windows; SELinux denying writes; running Composer as a user without write permission to the target directory.
Related errors
- Composer update failed: the "{tmpDir}" directory used to dow
- Composer rollback failed: "{oldFile}" could not be read
- {action} failed: "{localFilename}" could not be written. {er
- Failed to create junction to "%s" at "%s".
- Access to "{url}" is blocked.
AI-assisted analysis of composer/composer@6ffc117740 (2026-08-07).
Data as JSON: /api/errors/4699252156e05723.
Report an issue: GitHub.