Intervention/image · error · FileNotReadableException
Failed to open file from path "' . $path . '"
Error message
Failed to open file from path "' . $path . '"
What it means
File::fromPath() first validates the path via readableFilePathOrFail() (existence, readability) and then calls fopen(). If fopen() still returns false despite the passing pre-checks, this FileNotReadableException (a FilesystemException) is thrown. It signals a check-then-open race or an environment-level failure: the file was deleted or its permissions changed between the two calls, an open_basedir restriction interfered, or the process ran out of file descriptors.
Source
Thrown at src/File.php:56
}
/**
* {@inheritdoc}
*
* @see FileInterface::fromPath()
*
* @throws InvalidArgumentException
* @throws DirectoryNotFoundException
* @throws FileNotFoundException
* @throws FileNotReadableException
* @throws StreamException
*/
public static function fromPath(string $path): self
{
$stream = fopen(self::readableFilePathOrFail($path), 'r');
if ($stream === false) {
throw new FileNotReadableException('Failed to open file from path "' . $path . '"');
}
return new self($stream);
}
/**
* {@inheritdoc}
*
* @see FileInterface::save()
*
* @throws InvalidArgumentException
* @throws DirectoryNotFoundException
* @throws FileNotWritableException
* @throws StreamException
*/
public function save(string $path): void
{
if ($path === '') {View on GitHub (pinned to 5598b9e397)
Solutions
- Make temp-file lifecycles exclusive to one worker (unique tempnames via tempnam(), delete only after processing)
- Catch FilesystemException around read operations and retry once or re-enqueue the job
- Check open_basedir in php.ini and raise the open-file limit (ulimit -n) for long-running workers
- Verify the file still exists and is readable immediately before the call to shrink the race window
Example fix
// before $file = File::fromPath($sharedTmpPath); // another worker unlinks it concurrently // after $path = tempnam(sys_get_temp_dir(), 'img_'); copy($sharedTmpPath, $path); // private copy $file = File::fromPath($path);
Defensive patterns
Strategy: try-catch
Validate before calling
if (!is_string($path) || !is_file($path) || !is_readable($path)) {
throw new RuntimeException('Image file missing or unreadable: ' . $path);
}
// note: a race can still occur between this check and the internal fopen() Try / catch
use Intervention\Image\Exceptions\FilesystemException;
try {
$file = File::fromPath($path);
} catch (FilesystemException $e) {
// covers FileNotFoundException, FileNotReadableException and this race case
Logger::warning('Failed to open image file', ['path' => $path]);
throw new RuntimeException('Image source unavailable', 0, $e);
} Prevention
- Give each worker its own temp file (tempnam/copy) instead of sharing
- Delay cleanup jobs until no worker can still reference the files
- Monitor open-file limits (ulimit -n) for parallel image-processing workers
When it happens
Trigger: Two processes handling the same temporary file where one unlinks it between Intervention's readability check and the fopen() call; permissions being revoked mid-request; a process at its open-file limit (ulimit -n); PHP running with an open_basedir boundary that excludes the real path.
Common situations: High-concurrency queue workers processing shared temp files that a cleanup job deletes; tmpwatch/systemd-tmpfiles removing files during long-running requests; shared hosting with restrictive open_basedir; fd exhaustion under heavy parallel image processing.
Related errors
- Can't write to path. Directory "' . $dir . '" does not exist
- Can't write to path. Directory "' . $dir . '" is not writabl
- Can't write to path. Existing file " . $path . " is not writ
- Failed to write file to path " . $path
- Unable to analyze image colors
AI-assisted analysis of Intervention/image@5598b9e397 (2026-08-23).
Data as JSON: /api/errors/9db44e0ca0db2aae.
Report an issue: GitHub.