toeverything/AFFiNE · error · CopilotSessionInvalidInput
copilot_session_invalid_input
copilot_session_invalid_input
Error message
Cannot update action: ${session.id} What it means
CopilotSessionInvalidInput thrown by update() when internalCall is false (the default) and the loaded session has a non-null promptAction - an 'action session' created to serve a prompt action. Action sessions are system-managed; the public update path refuses to modify them at all. The guard checks the stored session, not the payload, so even a harmless title or pin update is rejected.
Solutions
- Skip update() for action sessions - filter them out of editable lists in the UI/API layer
- If you are implementing trusted server logic that genuinely must modify the session, pass update(options, true)
- Create a new normal session instead of editing the action session
Example fix
// before
await sessions.update({ userId, sessionId, title }); // sessionId belongs to an action session
// after
const session = await sessions.getExists(sessionId, { promptAction: true }, { userId });
if (session?.promptAction) {
// action sessions are system-managed; skip user-driven updates
} else {
await sessions.update({ userId, sessionId, title });
} Defensive patterns
Strategy: validation
Validate before calling
const session = await sessions.getExists(sessionId, { promptAction: true }, { userId });
if (session?.promptAction) {
// action sessions are system-managed: skip user-driven updates
} else {
await sessions.update({ userId, sessionId, title });
} Type guard
type ActionSession = { promptAction: string };
const isActionSession = (
s: { promptAction?: string | null }
): s is ActionSession => !!s.promptAction; Try / catch
try {
await sessions.update(options);
} catch (e) {
if (e instanceof CopilotSessionInvalidInput && e.message.startsWith('Cannot update action')) {
return; // system-managed session: ignore user edit
}
throw e;
} Prevention
- Filter action sessions out of editable chat lists in the UI
- Reserve internalCall = true for trusted server code paths only
- Expose promptAction on the session DTO so clients can branch
When it happens
Trigger: Calling update(options) without passing internalCall = true on a session whose aiSession.promptAction column is set (the session was created from an action prompt).
Common situations: Chat UI listing action sessions next to normal chats and offering rename/pin; a generic 'update session' code path that runs against every session id the client holds.
Related errors
- copilot_prompt_invalid
- action_bridge_stream_error
- copilot_session_deleted
- copilot_session_not_found
- image_format_not_supported
AI-assisted analysis of toeverything/AFFiNE@2af30773ae (2026-08-18).
Data as JSON: /api/errors/1d5813ccbc254d69.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/models/copilot-session.ts:724
sessionId,
{
id: true,
workspaceId: true,
docId: true,
parentSessionId: true,
pinned: true,
promptAction: true,
},
{ userId, workspaceId: options.workspaceId }
);
if (!session) {
throw new CopilotSessionNotFound();
}
// not allow to update action session
if (!internalCall) {
if (session.promptAction) {
throw new CopilotSessionInvalidInput(
`Cannot update action: ${session.id}`
);
} else if (docId && session.parentSessionId) {
throw new CopilotSessionInvalidInput(
`Cannot update docId for forked session: ${session.id}`
);
}
}
let nextPromptAction: string | null | undefined;
if (promptName) {
nextPromptAction = options.promptAction;
if (nextPromptAction === undefined) {
throw new CopilotSessionInvalidInput(
`Prompt action is required when changing prompt ${promptName}`
);
}
if (nextPromptAction) {View on GitHub (pinned to 2af30773ae)