{"record":{"id":"b882d04b826d4f0c","repo":"toeverything/AFFiNE","slug":"invalid-delegated-editor-session","errorCode":null,"errorMessage":"INVALID_DELEGATED_EDITOR_SESSION","messagePattern":"INVALID_DELEGATED_EDITOR_SESSION","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/delegated/realtime.ts","lineNumber":89,"sourceCode":"            ])\n          )\n          .max(4),\n      })\n      .strict();\n    this.registry.registerRequest({\n      name: 'copilot.delegated.editor.upsert',\n      input: leaseInput,\n      handle: async (user, input, context) => {\n        const session = await this.sessions.get(input.sessionId);\n        if (\n          !user ||\n          !context?.connectionId ||\n          !session ||\n          session.config.userId !== user.id ||\n          session.config.workspaceId !== input.workspaceId ||\n          session.config.docId !== input.docId\n        ) {\n          throw new Error('INVALID_DELEGATED_EDITOR_SESSION');\n        }\n        const lease = this.delegated.upsert(\n          user.id,\n          context.connectionId,\n          input\n        );\n        this.event.broadcast('copilot.delegated.editor.upserted', lease);\n        return { ok: true, expiresAt: lease.expiresAt };\n      },\n    });\n    this.registry.registerRequest({\n      name: 'copilot.delegated.editor.release',\n      input: z\n        .object({\n          clientId: z.string().min(1).max(128),\n          editorStateId: z.string().min(1).max(128),\n        })\n        .strict(),","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/delegated/realtime.ts#L71-L107","documentation":"Generic Error('INVALID_DELEGATED_EDITOR_SESSION') thrown by the copilot.delegated.editor.upsert realtime request handler when any of the session invariants fail: no authenticated user, missing connectionId in context, session lookup fails, or the session's userId/workspaceId/docId do not exactly match the input. It protects delegated-editor leases so a lease can only be created against the caller's own session for the exact doc in the request.","triggerScenarios":"Sending copilot.delegated.editor.upsert with a sessionId belonging to another user; passing workspaceId or docId that differs from session.config; a dropped/reconnected websocket so context.connectionId is missing; session expired and evicted between listing and upsert.","commonSituations":"Client state desyncs after doc switch (docId in input is stale); reconnect logic resends the upsert with an old session; concurrent tabs each hold different session ids.","solutions":["Ensure input.sessionId/workspaceId/docId are read from the same live session object the server returned","Re-establish the realtime connection (new connectionId) after reconnect before sending lease upserts","Re-fetch the copilot session and retry the upsert once with fresh values","Handle this error by tearing down the local delegated-editor lease and re-negotiating the session"],"exampleFix":"// before\nsocket.request('copilot.delegated.editor.upsert', {\n  sessionId: cachedSessionId, workspaceId: cachedWsId, docId: cachedDocId, // possibly stale\n});\n\n// after\nconst session = await refetchCurrentSession();\nif (!session) throw new Error('session gone');\nawait socket.request('copilot.delegated.editor.upsert', {\n  sessionId: session.id,\n  workspaceId: session.config.workspaceId,\n  docId: session.config.docId,\n});","handlingStrategy":"validation","validationCode":"const session = await chatSession.get(sessionId);\nif (\n  !session ||\n  session.config.userId !== user.id ||\n  session.config.workspaceId !== input.workspaceId ||\n  session.config.docId !== input.docId\n) {\n  throw new Error('Session/context mismatch — refetch before leasing');\n}\nif (!socket.isConnected || !socket.connectionId) await socket.reconnect();","typeGuard":null,"tryCatchPattern":"try {\n  await socket.request('copilot.delegated.editor.upsert', input);\n} catch (e) {\n  if (e.message === 'INVALID_DELEGATED_EDITOR_SESSION') {\n    await refetchSessionAndReconnect();\n    await socket.request('copilot.delegated.editor.upsert', refreshedInput); // one retry with fresh state\n  } else throw e;\n}","preventionTips":["Derive sessionId/workspaceId/docId from one server-returned session object, never from separate caches","Resend lease upserts only after the socket re-authenticates following a reconnect","Release leases on doc switch to avoid stacking stale lease requests"],"tags":["copilot","realtime","websocket","session","delegated-editor"],"backgroundTag":"session-validation-failed","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}