toeverything/AFFiNE · error · CopilotPromptInvalid
copilot_prompt_invalid
copilot_prompt_invalid
Error message
${promptName} are not allowed for ${sessionType} sessions What it means
CopilotPromptInvalid (invalid_input / copilot_prompt_invalid) thrown by CopilotSessionModel.checkSessionPrompt (packages/backend/server/src/models/copilot-session.ts:368). Sessions are classified Workspace / Pinned / Doc by getSessionType (pinned -> Pinned; no docId -> Workspace; else Doc). Workspace and Pinned sessions are not permitted to carry an action prompt — only Doc sessions are. If promptAction is a non-empty trimmed string for those session types, the guard throws.
Source
Thrown at packages/backend/server/src/models/copilot-session.ts:368
getSessionType(session: Pick<ChatSession, 'docId' | 'pinned'>): SessionType {
if (session.pinned) return SessionType.Pinned;
if (!session.docId) return SessionType.Workspace;
return SessionType.Doc;
}
checkSessionPrompt(
session: Pick<ChatSession, 'docId' | 'pinned'>,
prompt: Partial<ChatPrompt>
): boolean {
const sessionType = this.getSessionType(session);
const { name: promptName, action: promptAction } = prompt;
// workspace and pinned sessions cannot use action prompts
if (
[SessionType.Workspace, SessionType.Pinned].includes(sessionType) &&
!!promptAction?.trim()
) {
throw new CopilotPromptInvalid(
`${promptName} are not allowed for ${sessionType} sessions`
);
}
return true;
}
@Transactional()
async create(state: ChatSession, reuseChat = false): Promise<string> {
// find and return existing session if session is chat session
if (reuseChat && !state.promptAction) {
const sessionId = await this.find(state);
if (sessionId) return sessionId;
}
if (state.pinned) {
await this.unpin(state.workspaceId, state.userId);
}View on GitHub (pinned to 26c515e050)
Solutions
- Target a Doc session: pass a valid docId and ensure pinned is false when sending an action prompt.
- Strip the action from the prompt before sending to a Workspace or Pinned session.
- On the client, gate the action UI on getSessionType(session) === 'doc' before submit.
- If pinning is required, refactor so the action runs against the underlying doc session, not the pinned wrapper.
Example fix
// before
sessionModel.checkSessionPrompt({ docId: null, pinned: false }, { name: 'summary', action: 'summarize' });
// after
sessionModel.checkSessionPrompt({ docId: doc.id, pinned: false }, { name: 'summary', action: 'summarize' }); Defensive patterns
Strategy: validation
Validate before calling
const sessionType = sessionModel.getSessionType({ docId, pinned });
const allowed = sessionType !== 'workspace' && sessionType !== 'pinned';
if (!allowed && prompt.action?.trim()) {
throw new Error(`Action prompts not allowed for ${sessionType} sessions`);
}
sessionModel.checkSessionPrompt({ docId, pinned }, prompt); Type guard
const allowsAction = (s: { docId: string | null; pinned: boolean }): boolean =>
!s.pinned && !!s.docId; Try / catch
try {
sessionModel.checkSessionPrompt(session, prompt);
} catch (e) {
if (e instanceof UserFriendlyError && e.code === 'copilot_prompt_invalid') {
ui.warn('Actions require a doc session.');
return;
}
throw e;
} Prevention
- Bind action prompts to a docId; never send them at workspace root or on pinned sessions.
- Hide the action UI when getSessionType !== 'doc'.
- Strip prompt.action when reusing a workspace/pinned session object.
When it happens
Trigger: Creating or reusing a Copilot session whose sessionType is Workspace or Pinned while supplying a ChatPrompt with a non-empty action. Concretely: state.pinned === true OR state.docId is empty, combined with prompt.action.trim() !== ''.
Common situations: Frontend reuses a workspace-level chat session object but attaches a doc-scoped action (e.g. 'summarize'); pinning a session that previously had an action; sending an action prompt against the workspace root rather than a doc.
Related errors
- copilot_selected_sources_limit_exceeded
- copilot_session_deleted
- copilot_session_not_found
- copilot_session_invalid_input
- Failed to read image size
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/754e806f85b8b7b3.
Report an issue: GitHub.