{"record":{"id":"67b5653e8372a55c","repo":"m1k1o/neko","slug":"receiver-session-id-not-found","errorCode":null,"errorMessage":"receiver session ID not found","messagePattern":"receiver session ID not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/internal/websocket/handler/send.go","lineNumber":14,"sourceCode":"package handler\n\nimport (\n\t\"errors\"\n\n\t\"github.com/m1k1o/neko/server/pkg/types\"\n\t\"github.com/m1k1o/neko/server/pkg/types/event\"\n\t\"github.com/m1k1o/neko/server/pkg/types/message\"\n)\n\nfunc (h *MessageHandlerCtx) sendUnicast(session types.Session, payload *message.SendUnicast) error {\n\treceiver, ok := h.sessions.Get(payload.Receiver)\n\tif !ok {\n\t\treturn errors.New(\"receiver session ID not found\")\n\t}\n\n\treceiver.Send(\n\t\tevent.SEND_UNICAST,\n\t\tmessage.SendUnicast{\n\t\t\tSender:   session.ID(),\n\t\t\tReceiver: receiver.ID(),\n\t\t\tSubject:  payload.Subject,\n\t\t\tBody:     payload.Body,\n\t\t})\n\n\treturn nil\n}\n\nfunc (h *MessageHandlerCtx) sendBroadcast(session types.Session, payload *message.SendBroadcast) error {\n\th.sessions.Broadcast(\n\t\tevent.SEND_BROADCAST,\n\t\tmessage.SendBroadcast{","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/websocket/handler/send.go#L1-L32","documentation":"sendUnicast relays a private message from one session to another over the neko websocket. Before delivering, it looks up the target session by ID in the session manager (h.sessions.Get); if no session with payload.Receiver exists it throws this error and the message is not delivered. It is a client-addressing error: the sender referenced a session that has disconnected or never existed.","triggerScenarios":"Client sends a SEND_UNICAST websocket message whose 'receiver' field contains a session ID that is not currently registered — e.g. the target user disconnected, the ID was mistyped or stale, or the message races with the target's disconnect.","commonSituations":"Chat/plugins sending private messages to a user who just left the room; caching a peer's session ID across reconnects (IDs change on reconnect); broadcasting to a hardcoded ID; race between fetching a member list and sending.","solutions":["Check the receiver session ID is current: fetch the live session list and verify the target session still exists before sending","Fall back to a broadcast message if the target is gone, or surface 'user not in room' to the sender","Re-fetch session IDs after any reconnect instead of caching old ones","Handle the error per-message (log/skip) instead of failing the whole send loop"],"exampleFix":"// before\nh.websocket.Send(types.Message{Payload: message.SendUnicast{Receiver: cachedID, ...}})\n// after\nif _, ok := sessionList[cachedID]; ok {\n  h.websocket.Send(types.Message{Payload: message.SendUnicast{Receiver: cachedID, ...}})\n} else {\n  notifySender(\"receiver not in room\")\n}","handlingStrategy":"validation","validationCode":"func canSend(sessions types.SessionManager, receiverID string) bool {\n  _, ok := sessions.Get(receiverID)\n  return ok\n}","typeGuard":"func receiverExists(sessions types.SessionManager, id string) bool {\n  _, ok := sessions.Get(id)\n  return ok\n}","tryCatchPattern":"err := handler.Send(...)\nif err != nil && err.Error() == \"receiver session ID not found\" {\n  // drop message or notify sender that the user is offline\n}","preventionTips":["Always fetch fresh session IDs from the session manager before unicast","Never cache session IDs across reconnects","Treat unicast as best-effort and handle per-message failures","Verify membership before sending targeted messages"],"tags":["websocket","session","neko"],"backgroundTag":"session-not-found","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}