{"record":{"id":"09fa62b5b7643494","repo":"toeverything/AFFiNE","slug":"copilot-selected-sources-limit-exceeded","errorCode":"copilot_selected_sources_limit_exceeded","errorMessage":"Too many or too much content was selected. Select fewer sources and try again.","messagePattern":"Too many or too much content was selected\\. Select fewer sources and try again\\.","errorType":"exception","errorClass":"CopilotSelectedSourcesLimitExceeded","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/core/backend-runtime/job.ts","lineNumber":39,"sourceCode":"const SELECTED_DOCUMENT_TEXT_BYTE_LIMIT = 16 * 1024 * 1024;\nconst SELECTED_DOCUMENT_PRIORITY = 1000;\nconst SELECTED_DOCUMENT_WAIT_MS = 90_000;\n\n@Injectable()\nexport class BackendRuntimeEmbeddingService {\n  constructor(\n    private readonly rt: BackendRuntimeProvider,\n    private readonly models: Models\n  ) {}\n\n  async syncDocument(workspaceId: string, docId: string) {\n    await this.syncDocuments(workspaceId, [docId], true);\n  }\n\n  async prepareSelectedDocuments(workspaceId: string, docIds: string[]) {\n    const selectedDocIds = [...new Set(docIds)];\n    if (selectedDocIds.length > SELECTED_DOCUMENT_LIMIT) {\n      throw new CopilotSelectedSourcesLimitExceeded();\n    }\n    try {\n      await this.syncDocuments(workspaceId, selectedDocIds, false, {\n        priority: SELECTED_DOCUMENT_PRIORITY,\n        waitForReadyMs: SELECTED_DOCUMENT_WAIT_MS,\n      });\n    } catch (error) {\n      throw this.mapSelectedSourceError(error);\n    }\n  }\n\n  private mapSelectedSourceError(error: unknown) {\n    const message = error instanceof Error ? error.message : String(error);\n    if (message.includes('embedding_selected_sources_processing')) {\n      return new CopilotSelectedSourcesProcessing();\n    }\n    if (message.includes('embedding_selected_sources_failed')) {\n      return new CopilotSelectedSourcesFailed();","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/backend-runtime/job.ts#L21-L57","documentation":"Thrown by BackendRuntimeEmbeddingJob.prepareSelectedDocuments (job.ts:90-91) when a Copilot chat turn uses retrieval scope mode 'required' and the count of unique selected doc ids exceeds SELECTED_DOCUMENT_LIMIT (64). It is an 'invalid_input' UserFriendlyError raised before any embedding sync or model call starts, meaning the request was rejected purely on selection size.","triggerScenarios":"A chat turn whose scopeSnapshot.retrieval.mode === 'required' passes scope.requiredDocIds to prepareSelectedDocuments; after server-side dedupe ([...new Set(docIds)]) the distinct count is greater than 64.","commonSituations":"Selecting an entire folder or workspace as AI context in the doc picker; a UI 'select all' that forwards every doc id; assuming duplicates count against the limit (they are deduped first, so only distinct docs matter).","solutions":["Reduce the selection to at most 64 distinct docs and resubmit the turn.","In the context picker, deselect large or irrelevant docs; prefer a small focused set.","If more content is truly needed, split the work into multiple turns each with a subset of sources, or reduce per-doc content (see the separate 20,000-unit / 16MiB aggregate limits)."],"exampleFix":"// before\nconst requiredDocIds = allDocIds; // 120 docs -> copilot_selected_sources_limit_exceeded\nawait chat({ retrieval: { mode: 'required', requiredDocIds } });\n\n// after\nconst SELECTED_DOCUMENT_LIMIT = 64;\nconst requiredDocIds = [...new Set(allDocIds)].slice(0, SELECTED_DOCUMENT_LIMIT);\nawait chat({ retrieval: { mode: 'required', requiredDocIds } });","handlingStrategy":"validation","validationCode":"const SELECTED_DOCUMENT_LIMIT = 64;\nconst distinct = [...new Set(requiredDocIds)];\nif (distinct.length > SELECTED_DOCUMENT_LIMIT) {\n  throw new Error(`Select at most ${SELECTED_DOCUMENT_LIMIT} docs (got ${distinct.length})`);\n}\nawait prepareSelectedDocuments(workspaceId, distinct);","typeGuard":null,"tryCatchPattern":"try {\n  await prepareSelectedDocuments(workspaceId, requiredDocIds);\n} catch (e) {\n  if (getErrorCode(e) === 'copilot_selected_sources_limit_exceeded') {\n    // prompt user to shrink selection; do NOT retry unchanged\n  } else throw e;\n}","preventionTips":["Dedupe and cap the selection client-side at 64 distinct docs before submitting the turn.","Show a live count against the limit in the context picker UI.","Remember the limit is on distinct doc ids - dedupe first or you will overestimate."],"tags":["copilot","ai-retrieval","embedding","selection-limit","input-validation"],"backgroundTag":"context-size-limit-exceeded","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}