toeverything/AFFiNE · error · InvalidIndexerInput

invalid_indexer_input

invalid_indexer_input

Error message

Invalid indexer input: ${reason}

What it means

While building the search/aggregate DSL, the indexer service rejects pagination.limit > 10000 with InvalidIndexerInput (invalid_input, reason 'limit must be less than 10000'). The cap protects the search backend from oversized result sets and deep pagination.

Source

Thrown at packages/backend/server/src/plugins/indexer/service.ts:147

        if (!doc.title) doc.title = titles.get(doc.docId) ?? '';
      }
    }
    const users = await this.models.user.getPublicUsersMap(userIds);
    for (const doc of docs) {
      doc.createdByUser = users.get(doc.createdByUserId);
      doc.updatedByUser = users.get(doc.updatedByUserId);
    }
    return docs;
  }

  private unwrap<T>(output: SearchOperationOutput, workspaceId: string): T {
    if (output.ok) return output.value as T;
    switch (output.errorCode) {
      case 'workspace_denied':
        throw new SpaceAccessDenied({ spaceId: workspaceId });
      case 'invalid_request':
      case 'unsupported_query':
        throw new InvalidIndexerInput({ reason: output.errorCode });
      case 'provider_unavailable':
        throw new SearchProviderNotFound();
      case 'permission_unavailable':
        throw new WorkspacePermissionNotFound({ spaceId: workspaceId });
      default:
        throw new InternalServerError();
    }
  }
}

View on GitHub (pinned to 591f874dad)

Solutions

  1. Clamp the limit to 10000 (or omit it entirely to use the default page size)
  2. Use pagination.cursor for iterating large result sets instead of one big limit
  3. For exports, loop with skip/cursor and accumulate results

Example fix

// before
const dsl = service.search({ table, query, options: { pagination: { limit: totalCount } } });

// after
const MAX = 10000;
const dsl = service.search({ table, query, options: { pagination: { limit: Math.min(totalCount, MAX) } } });
Defensive patterns

Strategy: validation

Validate before calling

const MAX_LIMIT = 10000;
const limit = Math.min(requestedLimit ?? DEFAULT_PAGE, MAX_LIMIT);
const dsl = service.search({ table, query, options: { pagination: { limit } } });

Type guard

const isValidLimit = (limit: number | undefined): boolean =>
  limit === undefined || (Number.isInteger(limit) && limit <= 10000 && limit > 0);

Try / catch

try {
  return await search(input);
} catch (e) {
  if (e?.code === 'invalid_indexer_input' && /limit/.test(e.reason)) {
    input.options.pagination.limit = 10000;
    return search(input);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing options.pagination.limit greater than 10000 to the indexer search or aggregate APIs - typically code that passes a total count (e.g. docs count from another query) straight in as the limit.

Common situations: 'Fetch everything' export code paths; clients computing limit = totalCount; migrating from an API that had no cap.

Related errors


AI-assisted analysis of toeverything/AFFiNE@591f874dad (2026-08-21). Data as JSON: /api/errors/58821102ffbe69b5. Report an issue: GitHub.