symfony/process · error · InvalidArgumentException

" " yielded a value of type " ", but only scalars and…

Error message

"%s" yielded a value of type "%s", but only scalars and stream resources are supported.

What it means

When a Process is given an iterable input (e.g. a generator), each yielded value must be a string, a scalar castable to string, or a stream resource. If the iterator yields an object, array, null, or other non-scalar non-resource value, AbstractPipes::write throws InvalidArgumentException because such a value cannot be written to the process's stdin pipe.

Solutions

  1. Cast or serialize values before yielding: yield json_encode($row) instead of yield $row.
  2. Ensure yielded values are string, int, float, bool, or a stream resource.
  3. Map the iterable before passing it: new Process($cmd, null, null, (function () { foreach ($rows as $r) yield (string) $r; })()).
  4. If yielding null to signal end-of-input, stop iterating instead — the pipes layer handles iterator exhaustion itself.

Example fix

// before
function rows(): Generator { foreach ($data as $row) yield $row; } // yields arrays
$process = new Process($cmd, null, null, rows());

// after
function rows(): Generator { foreach ($data as $row) yield json_encode($row); }
$process = new Process($cmd, null, null, rows());
Defensive patterns

Strategy: type-guard

Validate before calling

$values = is_iterable($input) ? iterator_to_array((function () use ($input) { yield from $input; })()) : [$input];
foreach ($values as $v) {
    if (!is_scalar($v) && !is_resource($v) && null !== $v) {
        throw new \InvalidArgumentException('Input iterable must yield scalars or stream resources, got: '.get_debug_type($v));
    }
}

Type guard

function isValidProcessInput(mixed $v): bool {
    return is_string($v) || is_scalar($v) || is_resource($v);
}

Try / catch

try {
    $process->run();
} catch (\InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'only scalars and stream resources are supported')) {
        // inspect the input generator: something yielded a non-scalar
    }
    throw $e;
}

Prevention

When it happens

Trigger: Passing a Traversable/generator to Process::setInput or the $input constructor argument where a yielded value is not scalar and not a resource, e.g. yielding arrays or objects; the exception fires during readAndWrite while pumping input to a running process.

Common situations: Feeding a generator of decoded JSON entries (arrays) instead of encoded strings; yielding ORM entities or DTO objects; forgetting to json_encode/serialize before yielding.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of symfony/process@99b85026db (2026-09-14). Data as JSON: /api/errors/373c6656e8e986ff. Report an issue: GitHub.

Appendix: source

Thrown at Pipes/AbstractPipes.php:116

     *
     * @throws InvalidArgumentException When an input iterator yields a non supported value
     */
    protected function write(): ?array
    {
        if (!isset($this->pipes[0])) {
            return null;
        }
        $input = $this->input;

        if ($input instanceof \Iterator) {
            if (!$input->valid()) {
                $input = null;
            } elseif (\is_resource($input = $input->current())) {
                stream_set_blocking($input, false);
            } elseif (!isset($this->inputBuffer[0])) {
                if (!\is_string($input)) {
                    if (!\is_scalar($input)) {
                        throw new InvalidArgumentException(\sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input)));
                    }
                    $input = (string) $input;
                }
                $this->inputBuffer = $input;
                $this->input->next();
                $input = null;
            } else {
                $input = null;
            }
        }

        $r = $e = [];
        $w = [$this->pipes[0]];

        // let's have a look if something changed in streams
        if (false === @stream_select($r, $w, $e, 0, 0)) {
            return null;
        }

View on GitHub (pinned to 99b85026db)