w7corp/easywechat · error · RuntimeException

Invalid event type.

Error message

Invalid event type.

What it means

Thrown by Pay/Message::getEventType() when the decoded webhook message's original attributes have no string event_type field. WeChat Pay V3 notifications always carry event_type (e.g. TRANSACTION.SUCCESS); getting a non-string means the Message was built from something that is not a V3 event notification.

Source

Thrown at src/Pay/Message.php:34

    /**
     * @return array<string, mixed>
     */
    public function getOriginalAttributes(): array
    {
        $attributes = json_decode($this->getOriginalContents(), true);

        return is_array($attributes) ? $attributes : [];
    }

    /**
     * @throws RuntimeException
     */
    public function getEventType(): ?string
    {
        $eventType = $this->getOriginalAttributes()['event_type'];

        if (! is_string($eventType)) {
            throw new RuntimeException('Invalid event type.');
        }

        return $eventType;
    }
}

View on GitHub (pinned to f0cf0a8b83)

Solutions

  1. Branch on request Content-Type and only call getEventType()/handle() for V3 JSON notifications; route V2 XML through handlePaidCallback/decodeXmlMessage flow
  2. Guard before use: check $message->getOriginalAttributes()['event_type'] exists and is a string
  3. Log the raw body when it fails to identify what actually arrived

Example fix

// before
$message = $server->getRequestMessage();
$type = $message->getEventType(); // RuntimeException on V2/odd payloads
// after
$attrs = $message->getOriginalAttributes();
$type = isset($attrs['event_type']) && is_string($attrs['event_type'])
    ? $message->getEventType()
    : null;
if ($type === null) { /* not a V3 event notification: route accordingly */ }
Defensive patterns

Strategy: type-guard

Validate before calling

$attrs = $message->getOriginalAttributes();
if (! isset($attrs['event_type']) || ! is_string($attrs['event_type'])) {
    // not a V3 event notification - skip event routing
    return 'success';
}

Type guard

function isV3EventNotification(\EasyWeChat\Pay\Message $m): bool
{
    $t = $m->getOriginalAttributes()['event_type'] ?? null;
    return is_string($t) && $t !== '';
}

Try / catch

try {
    $type = $message->getEventType();
} catch (\EasyWeChat\Kernel\Exceptions\RuntimeException $e) {
    if ($e->getMessage() === 'Invalid event type.') {
        // log raw body, ack with success to avoid redelivery storms
        return 'success';
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling $message->getEventType() (or using Server::handle() routing which matches on event type) on a message whose origin body lacks event_type: V2 XML callbacks parsed into the same Message class, a plain API response payload reused as Message, or a manually constructed Message from test fixtures.

Common situations: Pointing the same handler at both V2 (XML) and V3 (JSON) webhooks; unit tests feeding minimal arrays; calling getRequestMessage() on a verification request instead of a real event; payload mangled by middleware that rewrote the raw body.

Related errors


AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21). Data as JSON: /api/errors/2f0b2e3b1fd4d8a8. Report an issue: GitHub.