walkor/workerman · error · RuntimeException

The $outputStream must to be a stream, %s given

Error message

The $outputStream must to be a stream, %s given

What it means

Worker::initStdOut() validates the configured output stream: it must be a live resource of type 'stream' (the place Workerman prints status/log output to). When the property holds something else (a string path, an array, a closed resource) it resets to the default stream and throws, naming the wrong type via get_debug_type.

Source

Thrown at src/Worker.php:682

            $iniFilePath = (string)php_ini_loaded_file();
            exit('Notice: '. implode(',', $disabled) . " are disabled by disable_functions. " . PHP_EOL
                . "Please remove them from disable_functions in $iniFilePath" . PHP_EOL);
        }
    }

    /**
     * Init stdout.
     *
     * @return void
     */
    protected static function initStdOut(): void
    {
        $defaultStream = fn () => defined('STDOUT') ? STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w'));
        static::$outputStream ??= $defaultStream(); //@phpstan-ignore-line
        if (!is_resource(self::$outputStream) || get_resource_type(self::$outputStream) !== 'stream') {
            $type = get_debug_type(self::$outputStream);
            static::$outputStream = $defaultStream();
            throw new RuntimeException(sprintf('The $outputStream must to be a stream, %s given', $type));
        }

        static::$outputDecorated ??= self::hasColorSupport();
    }

    /**
     * Borrowed from the symfony console
     * @link https://github.com/symfony/console/blob/0d14a9f6d04d4ac38a8cea1171f4554e325dae92/Output/StreamOutput.php#L92
     */
    private static function hasColorSupport(): bool
    {
        // Follow https://no-color.org/
        if (getenv('NO_COLOR') !== false) {
            return false;
        }

        if (getenv('TERM_PROGRAM') === 'Hyper') {
            return true;

View on GitHub (pinned to 1391112a61)

Solutions

  1. Assign a real resource: Worker::setStdOut(fopen('/var/log/app.log', 'a')) or leave it unset to use the default STDOUT
  2. If the stream comes from config, fopen() it first and check the result is a resource before assigning
  3. Do not close the stream you assigned; Workerman owns it for the process lifetime

Example fix

// before
Worker::$outputStream = '/var/log/app.log'; // string -> throws 'must be a stream, string given'

// after
Worker::setStdOut(fopen('/var/log/app.log', 'a'));
Defensive patterns

Strategy: validation

Validate before calling

$stream = fopen('/var/log/app.log', 'a');
if ($stream === false || !is_resource($stream) || get_resource_type($stream) !== 'stream') {
    throw new RuntimeException('cannot open log stream, keeping default stdout');
}
Worker::setStdOut($stream);

Type guard

function isUsableOutputStream(mixed $s): bool
{
    return is_resource($s) && get_resource_type($s) === 'stream';
}

Prevention

When it happens

Trigger: Setting Worker::$outputStream = 'php://stdout' or a file path string instead of a resource; assigning the result of a wrapper that returned false/null; passing a resource that was fclose()'d before Worker::runAll(); Worker::setStdOut() called with a non-resource.

Common situations: Trying to redirect worker output to a log file by assigning a path string; refactoring that swaps STDOUT (constant, resource) for a configurable value that arrives as a string from config; double-closing the stream in custom shutdown code.

Related errors


AI-assisted analysis of walkor/workerman@1391112a61 (2026-08-21). Data as JSON: /api/errors/6f06618533df12a1. Report an issue: GitHub.