guzzle/guzzle · error · \RuntimeException
Unable to save file %s
Error message
Unable to save file %s
What it means
Thrown by FileCookieJar::save() when file_put_contents() returns false, meaning the cookie JSON could not be written. The %s is the escaped filename (DiagnosticValue::escape is used to keep control bytes out of the message). This is an I/O failure: the path is unwritable, the directory does not exist, permissions are denied, or the disk is full.
Source
Thrown at src/Cookie/FileCookieJar.php:110
{
$json = [];
/** @var SetCookie $cookie */
foreach ($this as $cookie) {
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
$data = $cookie->toArray();
$data['HostOnly'] = $cookie->getHostOnly();
$json[] = $data;
}
}
try {
$jsonStr = \json_encode($json, \JSON_HEX_TAG | \JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
throw new \RuntimeException('Unable to encode cookie data', 0, $e);
}
if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) {
throw new \RuntimeException(\sprintf('Unable to save file %s', DiagnosticValue::escape($filename)));
}
// Best-effort: restrict the cookie file to the owner so persisted
// cookies are not world-readable.
@\chmod($filename, 0600);
}
/**
* Load cookies from a JSON formatted file.
*
* Old cookies are kept unless overwritten by newly loaded ones.
* Cookie records are constructed before any are passed to setCookie().
*
* @param string $filename Cookie file to load.
*
* @throws \RuntimeException if the file cannot be loaded or is invalid
*/
public function load(string $filename): voidView on GitHub (pinned to 9b200fc580)
Solutions
- Verify the directory exists and is writable by the PHP process user: is_dir(dirname($path)) && is_writable(dirname($path)).
- Use an absolute path under a known-writable directory (e.g. sys_get_temp_dir() or a dedicated var/ dir).
- Pre-create the file and chmod 0600 it; ensure the web user owns it.
- Catch \RuntimeException in __destruct context by calling save() explicitly in a try/catch (the destructor itself cannot meaningfully throw to the caller).
Example fix
// before
$jar = new FileCookieJar('/var/lock/cookies.json'); // not writable by app
// after
$path = dirname(__DIR__).'/var/cookies.json';
@touch($path); @chmod($path, 0600);
$jar = new FileCookieJar($path); Defensive patterns
Strategy: validation
Validate before calling
$dir = dirname($path);
if (!is_dir($dir) || !is_writable($dir)) {
throw new RuntimeException("Cookie dir not writable: $dir");
}
@touch($path); @chmod($path, 0600);
$jar = new FileCookieJar($path); Type guard
function cookieFileIsWritable(string $path): bool
{
$dir = dirname($path);
return is_dir($dir) && is_writable($dir)
&& (!file_exists($path) || is_writable($path));
} Try / catch
try {
$jar->save($path);
} catch (\RuntimeException $e) {
if (str_starts_with($e->getMessage(), 'Unable to save file')) {
// fall back to a temp dir or in-memory jar
}
} Prevention
- Pre-create and chmod 0600 the cookie file from your deployment script.
- Use an absolute path under a known-writable directory.
- Call save() explicitly (not just __destruct) so failures surface where you can handle them.
- Verify the PHP process user owns/has write access to the file.
When it happens
Trigger: Constructing `new FileCookieJar('/nonexistent/cookies.json')` then letting the destructor fire save(); pointing the jar at a directory that is not writable; running the script as a user without write permission to the file; SELinux/AppArmor denying the write; read-only filesystem.
Common situations: Deploying to a container where the target path is a read-only mounted volume; web user (www-data) lacking write permission on the cookies file; passing a relative path that resolves to an unexpected cwd; disk-full conditions.
Related errors
- Unable to load file %s
- Invalid cookie: %s
- Unable to encode cookie data
- Invalid cookie file: %s
- Unable to encode cookie data
AI-assisted analysis of guzzle/guzzle@9b200fc580 (2026-08-04).
Data as JSON: /data/errors/1a2ea1249921e5f2.json.
Report an issue: GitHub.