Skip to content

fix: move unreachable event cases out of the message content switch - #32

Merged
kkdai merged 1 commit into
masterfrom
fix/unreachable-event-cases
Sep 1, 2026
Merged

fix: move unreachable event cases out of the message content switch#32
kkdai merged 1 commit into
masterfrom
fix/unreachable-event-cases

Conversation

@kkdai

@kkdai kkdai commented Sep 1, 2026

Copy link
Copy Markdown
Owner

The bug

main.go handled four event types as cases on the inner switch:

switch e := event.(type) {
case webhook.MessageEvent:
    switch message := e.Message.(type) {   // <- switch over MessageContentInterface
    case webhook.TextMessageContent:    ...
    case webhook.StickerMessageContent: ...
    case webhook.MemberJoinedEvent:     // never reachable
    case webhook.MemberLeftEvent:       // never reachable
    case webhook.FollowEvent:           // never reachable
    case webhook.BeaconEvent:           // never reachable
    }
}

These are event types, not message contents, so they could never match. Normally the compiler rejects an impossible type switch case, but it can't here: MessageContentInterface is just

type MessageContentInterface interface { GetType() string }

and the event types happen to have a GetType() string too, so they satisfy it structurally. Four silently dead branches in a template people copy from.

A second bug underneath

Moving them to the outer switch is not enough — every case asserted e.Source.(webhook.UserSource), but Source is a SourceInterface whose concrete type depends on the chat. Member joined/left events only occur in group and multi-person chats, so the source is a GroupSource or RoomSource. The unchecked assertion panics, and a panic in an http.HandlerFunc kills the request:

interface conversion: webhook.SourceInterface is webhook.GroupSource, not webhook.UserSource

(reproduced against a real memberJoined payload — see TestOldCodePanics reasoning captured in TestMemberJoinedIsEventNotMessageContent).

The fix

  • Move FollowEvent, MemberJoinedEvent, MemberLeftEvent and BeaconEvent to the outer switch over cb.Events
  • Add sourceID to render user / group / room sources without asserting a single concrete type
  • Log the actual payload: joined/left members come from e.Joined / e.Left, the beacon hwid from e.Beacon — the source alone was never the interesting field
  • Guard the optional pointer sub-objects with orZero so a payload that omits them logs instead of nil-derefing
  • Fix the copy-pasted "Member joined" log message on the member left case
  • Reword the outer default from "Unsupported message: %T" to "Unsupported event: %T", which is what it actually reports

Tests

First tests in this repo. main_test.go decodes a real memberJoined webhook payload and pins down both facts the old code got wrong — that it is an event rather than a message content, and that its source is a GroupSource — plus table tests for sourceID, userIDs and the nil-pointer paths.

  • go build ./...
  • go vet ./...
  • gofmt -l . clean
  • go test ./... — 4 tests, all passing

Note

Rebased on top of master after #31 (SDK v8.22.0 + README) was merged, so CI here runs against Go 1.25 and line-bot-sdk-go v8.22.0.

MemberJoinedEvent, MemberLeftEvent, FollowEvent and BeaconEvent were
cases on the inner `switch e.Message.(type)`. They are event types, not
message contents, so they could never match at runtime. This compiled
only because MessageContentInterface is `GetType() string`, which the
event types happen to satisfy, so the compiler could not flag them as
impossible type switch cases.

Moving them to the outer switch over cb.Events exposes a second bug:
every case asserted `e.Source.(webhook.UserSource)`, but Source is a
SourceInterface whose concrete type depends on the chat. Member
joined/left only occur in group and multi-person chats, so the source is
a GroupSource or RoomSource and the assertion panics.

- Move the four cases to the outer event switch
- Add sourceID to describe user, group and room sources without panicking
- Report the joined/left members from e.Joined / e.Left rather than the
  source, and the hwid from e.Beacon
- Guard the optional pointer sub-objects with orZero so a payload that
  omits them logs instead of crashing
- Fix the copy-pasted "Member joined" log on the member left case
- Add tests covering the decoded payload shape and the helpers
@kkdai
kkdai force-pushed the fix/unreachable-event-cases branch from af6c195 to ffaeed7 Compare September 1, 2026 07:25
@kkdai
kkdai merged commit df8f942 into master Sep 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant