{"record":{"id":"b1d778dbc2d0769b","repo":"m1k1o/neko","slug":"webrtc-peer-does-not-exist","errorCode":null,"errorMessage":"webRTC peer does not exist","messagePattern":"webRTC peer does not exist","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/websocket/handler/signal.go","lineNumber":80,"sourceCode":"\t}\n\n\tsession.Send(\n\t\tevent.SIGNAL_PROVIDE,\n\t\tmessage.SignalProvide{\n\t\t\tSDP:        offer.SDP,\n\t\t\tICEServers: h.webrtc.ICEServers(),\n\n\t\t\tVideo: peer.Video(),\n\t\t\tAudio: peer.Audio(),\n\t\t})\n\n\treturn nil\n}\n\nfunc (h *MessageHandlerCtx) signalRestart(session types.Session) error {\n\tpeer := session.GetWebRTCPeer()\n\tif peer == nil {\n\t\treturn errors.New(\"webRTC peer does not exist\")\n\t}\n\n\toffer, err := peer.CreateOffer(true)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// TODO: Use offer event instead.\n\tsession.Send(\n\t\tevent.SIGNAL_RESTART,\n\t\tmessage.SignalDescription{\n\t\t\tSDP: offer.SDP,\n\t\t})\n\n\treturn nil\n}\n\nfunc (h *MessageHandlerCtx) signalOffer(session types.Session, payload *message.SignalDescription) error {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/websocket/handler/signal.go#L62-L98","documentation":"signalRestart asks the existing WebRTC peer to create a new offer (ICE restart). It fetches the peer via session.GetWebRTCPeer(); when no peer has been created for this session yet, it errors with 'webRTC peer does not exist'. Restarting implies a prior negotiation, so calling it before that state exists is invalid.","triggerScenarios":"Client sends a signal/restart message for a session whose peer was never created (signal/request was never answered), or whose peer was destroyed on disconnect/timeout.","commonSituations":"Web client reconnect logic firing 'restart' after the peer was torn down; network blip where the client restarts ICE but the server already closed the peer; duplicate restart after a failed negotiation.","solutions":["Send a fresh signal/request (full renegotiation) instead of signal/restart when no peer exists","Reconnect the websocket + WebRTC session entirely if the peer is gone","Ensure the client only issues restart after a successful prior offer/answer exchange","Handle this server response by falling back to creating a new peer"],"exampleFix":"// before\nws.send({event: 'signal/restart'})\n// after\nif (peerEstablished) ws.send({event: 'signal/restart'})\nelse ws.send({event: 'signal/request'})","handlingStrategy":"fallback","validationCode":"if session.GetWebRTCPeer() == nil {\n  // peer missing: use signal/request instead of signal/restart\n}","typeGuard":"func hasPeer(s types.Session) bool { return s.GetWebRTCPeer() != nil }","tryCatchPattern":"err := handler.SignalRestart(session)\nif err != nil && strings.Contains(err.Error(), \"webRTC peer does not exist\") {\n  // fall back to full renegotiation via signal/request\n}","preventionTips":["Only send restart after a completed offer/answer exchange","Fall back to signal/request when peer state is gone","Track peer lifetime client-side and reset state on disconnect","Avoid double restarts by debouncing restart triggers"],"tags":["webrtc","negotiation","neko"],"backgroundTag":"webrtc-peer-not-found","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}