{"record":{"id":"754e806f85b8b7b3","repo":"toeverything/AFFiNE","slug":"copilot-prompt-invalid","errorCode":"copilot_prompt_invalid","errorMessage":"${promptName} are not allowed for ${sessionType} sessions","messagePattern":"(.+?) are not allowed for (.+?) sessions","errorType":"error_code","errorClass":"CopilotPromptInvalid","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/models/copilot-session.ts","lineNumber":368,"sourceCode":"  getSessionType(session: Pick<ChatSession, 'docId' | 'pinned'>): SessionType {\n    if (session.pinned) return SessionType.Pinned;\n    if (!session.docId) return SessionType.Workspace;\n    return SessionType.Doc;\n  }\n\n  checkSessionPrompt(\n    session: Pick<ChatSession, 'docId' | 'pinned'>,\n    prompt: Partial<ChatPrompt>\n  ): boolean {\n    const sessionType = this.getSessionType(session);\n    const { name: promptName, action: promptAction } = prompt;\n\n    // workspace and pinned sessions cannot use action prompts\n    if (\n      [SessionType.Workspace, SessionType.Pinned].includes(sessionType) &&\n      !!promptAction?.trim()\n    ) {\n      throw new CopilotPromptInvalid(\n        `${promptName} are not allowed for ${sessionType} sessions`\n      );\n    }\n\n    return true;\n  }\n\n  @Transactional()\n  async create(state: ChatSession, reuseChat = false): Promise<string> {\n    // find and return existing session if session is chat session\n    if (reuseChat && !state.promptAction) {\n      const sessionId = await this.find(state);\n      if (sessionId) return sessionId;\n    }\n\n    if (state.pinned) {\n      await this.unpin(state.workspaceId, state.userId);\n    }","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/models/copilot-session.ts#L350-L386","documentation":"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.","triggerScenarios":"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() !== ''.","commonSituations":"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.","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."],"exampleFix":"// before\nsessionModel.checkSessionPrompt({ docId: null, pinned: false }, { name: 'summary', action: 'summarize' });\n// after\nsessionModel.checkSessionPrompt({ docId: doc.id, pinned: false }, { name: 'summary', action: 'summarize' });","handlingStrategy":"validation","validationCode":"const sessionType = sessionModel.getSessionType({ docId, pinned });\nconst allowed = sessionType !== 'workspace' && sessionType !== 'pinned';\nif (!allowed && prompt.action?.trim()) {\n  throw new Error(`Action prompts not allowed for ${sessionType} sessions`);\n}\nsessionModel.checkSessionPrompt({ docId, pinned }, prompt);","typeGuard":"const allowsAction = (s: { docId: string | null; pinned: boolean }): boolean =>\n  !s.pinned && !!s.docId;","tryCatchPattern":"try {\n  sessionModel.checkSessionPrompt(session, prompt);\n} catch (e) {\n  if (e instanceof UserFriendlyError && e.code === 'copilot_prompt_invalid') {\n    ui.warn('Actions require a doc session.');\n    return;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["copilot","session","prompt","validation"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}