{"id":"cf8b5f553c544b00","repo":"guzzle/guzzle","slug":"on-stats-must-be-callable-cf8b5f","errorCode":null,"errorMessage":"on_stats must be callable","messagePattern":"on_stats must be callable","errorType":"validation","errorClass":"GuzzleHttp\\Exception\\InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Handler/StreamHandler.php","lineNumber":192,"sourceCode":"            throw new RequestException('The stream handler cannot guarantee a multiplexed protocol; required multiplexing needs a cURL handler.', $request);\n        }\n\n        $protocolVersion = $request->getProtocolVersion();\n\n        if ('' === $protocolVersion) {\n            throw new RequestException('HTTP protocol version must not be empty.', $request);\n        }\n\n        if (1 !== \\preg_match('/^\\d+(?:\\.\\d+)?$/D', $protocolVersion)) {\n            throw new RequestException('HTTP protocol version must be a valid HTTP version number.', $request);\n        }\n\n        if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {\n            throw new RequestException(sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request);\n        }\n\n        if (isset($options['on_stats']) && !\\is_callable($options['on_stats'])) {\n            throw new InvalidArgumentException('on_stats must be callable');\n        }\n\n        $startTime = isset($options['on_stats']) ? Clock::now() : null;\n\n        self::rejectUnsupportedRequestOptions($request, $options);\n        $this->rejectStreamingWithConnectionCaps($options);\n        $this->assertTransportSharingSupported();\n\n        // The stream wrapper sends HEAD request bodies, unlike cURL's NOBODY\n        // path, so validate their framing normally.\n        $framing = RequestFraming::analyze($request->withoutHeader('Expect'));\n        $request = $framing->request;\n\n        try {\n            self::assertRequestUriSupported($request, $options);\n            $body = $framing->materialize();\n            if ($framing->contentLength === null && ($body !== '' || \\in_array($request->getMethod(), ['PUT', 'POST'], true))) {\n                $request = $request->withHeader('Content-Length', (string) \\strlen($body));","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/guzzle/guzzle/blob/9b200fc5805036b331d6031199880dadecae0275/src/Handler/StreamHandler.php#L174-L210","documentation":"Thrown by StreamHandler::__invoke when the 'on_stats' request option is set but is not callable. The handler invokes on_stats as a function with a TransferStats argument after each transfer, so a non-callable value would cause a fatal TypeError at invocation time; this guard fails fast at dispatch instead.","triggerScenarios":"Passing ['on_stats' => 'myFunc'] when the function is not defined/loaded; passing an array that is not a valid callable; passing an object lacking __invoke; passing a closure stored in a variable that is null.","commonSituations":"Refactoring that removes a stats callback but leaves the option key; namespaced code where the callback string lacks its namespace; conditional code paths that sometimes set on_stats to a non-callable.","solutions":["Pass a callable: a closure, an invokable object, or a valid [Class, 'method'] array.","For string callbacks, ensure the function exists and is in scope (use the fully-qualified name for namespaced functions).","Verify with is_callable($value) before assigning the option."],"exampleFix":"// before\n$client->request('GET', $url, ['on_stats' => 'logStats']); // logStats not defined\n// after\n$client->request('GET', $url, ['on_stats' => function (TransferStats $s) { /* ... */ }]);","handlingStrategy":"type-guard","validationCode":"if (isset($options['on_stats']) && !is_callable($options['on_stats'])) {\n    throw new \\InvalidArgumentException('on_stats must be callable');\n}","typeGuard":"function isOnStatsCallable(mixed $v): bool { return $v === null || is_callable($v); }","tryCatchPattern":null,"preventionTips":["Always pass a closure or a valid [Class, 'method'] array for on_stats.","For string callbacks use the fully-qualified function name in namespaced code.","Verify with is_callable() before assigning the option."],"tags":["stream-handler","on-stats","validation","callable"],"analyzedSha":"9b200fc5805036b331d6031199880dadecae0275","analyzedAt":"2026-08-04T21:24:26.648Z","schemaVersion":2}