toeverything/AFFiNE · warning · CopilotSelectedSourcesLimitExceeded

copilot_selected_sources_limit_exceeded

copilot_selected_sources_limit_exceeded

Error message

Too many or too much content was selected. Select fewer sources and try again.

What it means

Thrown by BackendRuntimeJob.prepareSelectedDocuments when the de-duplicated list of selected doc ids exceeds SELECTED_DOCUMENT_LIMIT (64). Category 'invalid_input', code 'copilot_selected_sources_limit_exceeded'. This is a hard count cap applied before any embedding sync work begins, to bound copilot context-selection cost.

Source

Thrown at packages/backend/server/src/core/backend-runtime/job.ts:91

    await this.queue.add(
      'backendRuntime.syncDocumentEmbedding',
      { workspaceId, docId },
      { jobId: `syncDocumentEmbedding/${workspaceId}/${docId}` }
    );
  }

  @OnJob('backendRuntime.syncDocumentEmbedding')
  async syncDocument({
    workspaceId,
    docId,
  }: Jobs['backendRuntime.syncDocumentEmbedding']) {
    await this.syncDocuments(workspaceId, [docId], true);
  }

  async prepareSelectedDocuments(workspaceId: string, docIds: string[]) {
    const selectedDocIds = [...new Set(docIds)];
    if (selectedDocIds.length > SELECTED_DOCUMENT_LIMIT) {
      throw new CopilotSelectedSourcesLimitExceeded();
    }
    try {
      await this.syncDocuments(workspaceId, selectedDocIds, false, {
        priority: SELECTED_DOCUMENT_PRIORITY,
        waitForReadyMs: SELECTED_DOCUMENT_WAIT_MS,
      });
    } catch (error) {
      throw this.mapSelectedSourceError(error);
    }
  }

  private mapSelectedSourceError(error: unknown) {
    const message = error instanceof Error ? error.message : String(error);
    if (message.includes('embedding_selected_sources_processing')) {
      return new CopilotSelectedSourcesProcessing();
    }
    if (message.includes('embedding_selected_sources_failed')) {
      return new CopilotSelectedSourcesFailed();

View on GitHub (pinned to 26c515e050)

Solutions

  1. Limit the selection to at most 64 distinct documents in the UI before calling the API.
  2. Replace 'select all' with a paginated/bounded multi-select capped at 64.
  3. Surface a clear 'select fewer sources' message when the user exceeds 64.

Example fix

// before
await prepareSelectedDocuments(wsId, allDocIds);

// after
const MAX = 64;
const ids = [...new Set(selectedIds)];
if (ids.length > MAX) {
  show(`Select at most ${MAX} sources (you chose ${ids.length}).`);
  return;
}
await prepareSelectedDocuments(wsId, ids);
Defensive patterns

Strategy: validation

Validate before calling

const MAX_SELECTED = 64;
const uniqueIds = [...new Set(docIds)];
if (uniqueIds.length > MAX_SELECTED) {
  throw new Error(`Select at most ${MAX_SELECTED} sources (got ${uniqueIds.length}).`);
}
await prepareSelectedDocuments(workspaceId, uniqueIds);

Type guard

function withinSelectedLimit(ids: string[]): boolean {
  return new Set(ids).size <= 64;
}

Prevention

When it happens

Trigger: Calling prepareSelectedDocuments(workspaceId, docIds) (job.ts:88-92) with more than 64 unique doc ids. Duplicates are collapsed via new Set, so the cap is on distinct documents.

Common situations: A 'select all' action in the UI grabbing an entire folder/workspace; bulk selection that bypasses the client-side count guard; an API client constructing the list from a search result without paging.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/09fa62b5b7643494. Report an issue: GitHub.