Intervention/image · error · InvalidArgumentException

Image source must be a resource of type 'file' or 'stream'

Error message

Image source must be a resource of type 'file' or 'stream'

What it means

StreamImageDecoder accepts only a PHP resource whose get_resource_type() is 'file' or 'stream'. Any other input (string, object, closed resource, or a resource of another type like a curl handle) triggers InvalidArgumentException before any reading starts.

Source

Thrown at src/Drivers/Gd/Decoders/StreamImageDecoder.php:43

        return is_resource($input);
    }

    /**
     * {@inheritdoc}
     *
     * @see DecoderInterface::decode()
     *
     * @throws InvalidArgumentException
     * @throws StreamException
     * @throws DriverException
     * @throws StateException
     * @throws ImageDecoderException
     * @throws NotSupportedException
     */
    public function decode(mixed $input): ImageInterface
    {
        if (!is_resource($input) || !in_array(get_resource_type($input), ['file', 'stream'])) {
            throw new InvalidArgumentException("Image source must be a resource of type 'file' or 'stream'");
        }

        $contents = '';
        $result = rewind($input);

        if ($result === false) {
            throw new StreamException('Failed to rewind position of stream');
        }

        while (!feof($input)) {
            $chunk = fread($input, 1024);
            if ($chunk === false) {
                throw new StreamException('Failed to read image from stream');
            }

            $contents .= $chunk;
        }

View on GitHub (pinned to 5598b9e397)

Solutions

  1. Pass a real resource from fopen($path, 'rb') or a php://temp handle
  2. For PSR-7 streams, use (string) $stream or $stream->getContents() and read the string instead
  3. For plain strings, call $manager->read($string) and let BinaryImageDecoder handle it

Example fix

// before
$image = $manager->decodeStream($response->getBody()); // object, not resource

// after
$stream = fopen('php://temp', 'rb+');
fwrite($stream, $response->getBody()->getContents());
rewind($stream);
$image = $manager->decodeStream($stream);
// or simply: $image = $manager->read($response->getBody()->getContents());
Defensive patterns

Strategy: type-guard

Validate before calling

if (!is_resource($stream) || !in_array(get_resource_type($stream), ['file', 'stream'], true)) {
    throw new InvalidArgumentException('Expected a file/stream resource');
}
$manager->decodeStream($stream);

Type guard

function isFileStream(mixed $value): bool
{
    return is_resource($value) && in_array(get_resource_type($value), ['file', 'stream'], true);
}

Try / catch

try {
    $image = $manager->decodeStream($source);
} catch (InvalidArgumentException $e) {
    $image = $manager->read((string) $source); // fall back to string decoding
}

Prevention

When it happens

Trigger: $manager->decodeStream($psr7Stream) where $psr7Stream is a PSR-7 StreamInterface object; passing a curl handle; passing a resource already closed by fclose(); passing a plain string path.

Common situations: Confusing Guzzle/Symfony stream objects with native PHP resources, frameworks whose file APIs return objects (Symfony UploadedFile), resources from proc_open or curl reused as image sources.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of Intervention/image@5598b9e397 (2026-08-23). Data as JSON: /api/errors/0d32ab8231f2cdff. Report an issue: GitHub.