{"record":{"id":"8997a4018593244e","repo":"w7corp/easywechat","slug":"invalid-handler-s","errorCode":null,"errorMessage":"Invalid handler: %s","messagePattern":"Invalid handler: (.+?)","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Kernel/Traits/InteractWithHandlers.php","lineNumber":76,"sourceCode":"        return [\n            'hash' => $this->getHandlerHash($handler),\n            'handler' => $this->makeClosure($handler),\n        ];\n    }\n\n    /**\n     * @throws InvalidArgumentException\n     */\n    protected function getHandlerHash(callable|array|string $handler): string\n    {\n        return match (true) {\n            is_string($handler) => $handler,\n            is_array($handler) => is_string($handler[0])\n                ? $handler[0].'::'.$handler[1]\n                : get_class($handler[0]).$handler[1],\n            $handler instanceof Closure => spl_object_hash($handler),\n            is_callable($handler) => spl_object_hash($handler),\n            default => throw new InvalidArgumentException('Invalid handler: '.gettype($handler)),\n        };\n    }\n\n    /**\n     * @throws InvalidArgumentException\n     */\n    protected function makeClosure(callable|string $handler): callable\n    {\n        if (is_callable($handler)) {\n            return $handler;\n        }\n\n        if (class_exists($handler) && method_exists($handler, '__invoke')) {\n            /**\n             * @psalm-suppress InvalidFunctionCall\n             *\n             * @phpstan-ignore-next-line https://github.com/phpstan/phpstan/issues/5867\n             */","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/w7corp/easywechat/blob/f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8/src/Kernel/Traits/InteractWithHandlers.php#L58-L94","documentation":"getHandlerHash() builds a dedup key for handlers registered via withHandler()/addMessageListener() etc. Strings map to themselves, [class, method] arrays to Class::method, closures and callables to spl_object_hash; anything else (int, float, bool, null, non-callable object — gettype() is appended to the message) hits the default branch and throws InvalidArgumentException. Public entry points type-hint callable|string, so in plain SDK usage PHP raises a TypeError first; this branch mainly fires in subclasses that loosen the signature or in direct getHandlerHash() calls.","triggerScenarios":"Calling getHandlerHash() directly with null/int/bool/non-callable objects; a subclass widening withHandler() to mixed and passing an uninitialized handler variable; data-driven handler maps where a config key is absent so null is registered.","commonSituations":"Optional handler variables defaulting to null; config-driven handler lists with missing keys; wrapping handlers in plain DTOs without __invoke.","solutions":["Pass a supported form: Closure, 'Class::method', [new Class, 'method'], or an invokable class name.","Null-check optional handlers before registering: if ($handler) { $server->withHandler($handler); }.","Validate config-driven handler maps with is_callable()/class_exists() before withHandler().","If you subclass Server, keep the callable|string signature so PHP rejects bad types early."],"exampleFix":"// before: optional handler that may be null\n$server->withHandler($config['fallback_handler']);\n// after: guard before registering\nif ($handler = $config['fallback_handler'] ?? null) {\n    $server->withHandler($handler);\n}","handlingStrategy":"type-guard","validationCode":"if ($handler !== null && (is_callable($handler) || (is_string($handler) && class_exists($handler)))) { $server->withHandler($handler); }","typeGuard":"function isRegistrableHandler(mixed $h): bool { return $h instanceof \\Closure || (is_string($h) && $h !== '') || (is_array($h) && isset($h[0], $h[1])) || (is_object($h) && is_callable($h)); }","tryCatchPattern":"try { $server->withHandler($handler); } catch (\\EasyWeChat\\Kernel\\Exceptions\\InvalidArgumentException $e) { if (str_starts_with($e->getMessage(), 'Invalid handler')) { throw new InvalidArgumentException('Handler map entry rejected: '.get_debug_type($handler), 0, $e); } throw $e; }","preventionTips":["Default optional handler configs to a concrete invokable class","Lint config-declared handlers at boot","Build handlers in one factory so bad types surface early"],"tags":["php","handlers","callable","invalid-argument"],"backgroundTag":"invalid-callback-handler","analyzedSha":"f0cf0a8b8361417ed683b8246d0ecbaf0aafcaa8","analyzedAt":"2026-08-21T05:29:19.565Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}