{"record":{"id":"b218e7b89351eda3","repo":"toeverything/AFFiNE","slug":"authentication-required-b218e7","errorCode":null,"errorMessage":"AUTHENTICATION_REQUIRED","messagePattern":"AUTHENTICATION_REQUIRED","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/backend/server/src/plugins/copilot/delegated/realtime.ts","lineNumber":136,"sourceCode":"    });\n    this.registry.registerRequest({\n      name: 'copilot.delegated.tool.respond',\n      input: responseSchema,\n      handle: async (user, response) => {\n        if (!user) return { accepted: false };\n        const accepted = this.delegated.receive(user.id, response);\n        this.event.broadcast('copilot.delegated.tool.responded', {\n          userId: user.id,\n          response,\n        });\n        return { accepted };\n      },\n    });\n    this.registry.registerTopic({\n      name: 'copilot.delegated.tool.requested',\n      input: z.object({ clientId: z.string().min(1).max(128) }).strict(),\n      authorize: async user => {\n        if (!user) throw new Error('AUTHENTICATION_REQUIRED');\n      },\n      room: (user, input) => {\n        if (!user) throw new Error('AUTHENTICATION_REQUIRED');\n        return realtimeUserRoom(user.id, `copilot:${input.clientId}`);\n      },\n    });\n  }\n}\n","sourceCodeStart":118,"sourceCodeEnd":145,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/delegated/realtime.ts#L118-L145","documentation":"Error('AUTHENTICATION_REQUIRED') thrown from the authorize callback of the copilot.delegated.tool.requested realtime topic when there is no authenticated user on the socket. Subscribing to this topic (which streams tool-call requests to a delegating client) is only valid for authenticated connections; the guard runs before the room is computed.","triggerScenarios":"Subscribing to topic copilot.delegated.tool.requested over an anonymous/unauthenticated websocket; token expired before subscribe; auth handshake skipped by a custom client.","commonSituations":"Client subscribes before completing the auth flow; session cookie/JWT expired and the socket silently downgraded to anonymous; load-test client forgot credentials.","solutions":["Authenticate the realtime connection (valid token/cookie) before subscribing to the topic","Re-authenticate and resubscribe when the token expires mid-session","In client code, gate topic subscription on the connection's authenticated state"],"exampleFix":"// before\nsocket.subscribe('copilot.delegated.tool.requested', { clientId }); // anonymous socket\n\n// after\nawait socket.authenticate(token);\nsocket.subscribe('copilot.delegated.tool.requested', { clientId });","handlingStrategy":"validation","validationCode":"if (!socket.isAuthenticated) {\n  await socket.authenticate(getToken());\n}\nawait socket.subscribe('copilot.delegated.tool.requested', { clientId });","typeGuard":null,"tryCatchPattern":"try {\n  await socket.subscribe('copilot.delegated.tool.requested', { clientId });\n} catch (e) {\n  if (e.message === 'AUTHENTICATION_REQUIRED') {\n    await reauthenticate();\n    await socket.subscribe('copilot.delegated.tool.requested', { clientId });\n  } else throw e;\n}","preventionTips":["Complete the auth handshake before any topic subscription","Refresh tokens proactively before expiry so the socket never downgrades to anonymous","Gate delegated-tool subscriptions behind the app's authenticated state"],"tags":["copilot","realtime","authentication","authorization"],"backgroundTag":"user-not-authenticated","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}