{"record":{"id":"da1e00f99c58857f","repo":"m1k1o/neko","slug":"not-allowed-to-watch","errorCode":null,"errorMessage":"not allowed to watch","messagePattern":"not allowed to watch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/websocket/handler/signal.go","lineNumber":15,"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\t\"github.com/pion/webrtc/v4\"\n)\n\nfunc (h *MessageHandlerCtx) signalRequest(session types.Session, payload *message.SignalRequest) error {\n\tif !session.Profile().CanWatch {\n\t\treturn errors.New(\"not allowed to watch\")\n\t}\n\n\toffer, peer, err := h.webrtc.CreatePeer(session)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// set webrtc as paused if session has private mode enabled\n\tif session.PrivateModeEnabled() {\n\t\tpeer.SetPaused(true)\n\t}\n\n\tvideo := payload.Video\n\n\t// use default first video, if not provided\n\tif video.Selector == nil {\n\t\tvideos := h.capture.Video().IDs()\n\t\tvideo.Selector = &types.StreamSelector{","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/websocket/handler/signal.go#L1-L33","documentation":"signalRequest starts a WebRTC negotiation so the user can watch the stream. It first checks session.Profile().CanWatch; if the authenticated member lacks the 'watch' permission it refuses with this error and no peer connection is created. It is an authorization error tied to the member profile configured by the host/admin.","triggerScenarios":"A user whose member profile has CanWatch=false sends a signal/request message; a host with restricted permissions tries to view the stream; permissions changed server-side while the client still assumes it can watch.","commonSituations":"Connecting as a host/limited-user account type that is not allowed to view streams; admin removed watch permission from the user's profile; a client (e.g. the web app) sends a watch request from a control-only session.","solutions":["Grant the session's member profile CanWatch=true (via admin API / members management) if the user should watch","Use an account/profile type that has watch permission","Update the client to not send signal requests from sessions without watch permission, and show a permission-denied UI instead","If permissions changed, reconnect so the session picks up the new profile"],"exampleFix":"// before (server config)\n{\"name\": \"limited\", \"canWatch\": false}\n// after\n{\"name\": \"limited\", \"canWatch\": true}","handlingStrategy":"validation","validationCode":"if !session.Profile().CanWatch {\n  // do not send signal/request\n  return errors.New(\"watch permission required\")\n}","typeGuard":"func canWatch(s types.Session) bool { return s.Profile().CanWatch }","tryCatchPattern":"err := handler.SignalRequest(...)\nif err != nil && strings.Contains(err.Error(), \"not allowed to watch\") {\n  // show permission-denied UI to the user\n}","preventionTips":["Check session profile permissions client-side before requesting the stream","Use account types with the right permissions for the task","Sync permission changes by reconnecting the session","Hide stream controls for users without watch permission"],"tags":["webrtc","permissions","authorization"],"backgroundTag":"permission-denied","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}