{"record":{"id":"4e8289481309fde0","repo":"yikart/AiToEarn","slug":"channelpaginationmodenotsupported","errorCode":"ChannelPaginationModeNotSupported","errorMessage":"ChannelPaginationModeNotSupported","messagePattern":"ChannelPaginationModeNotSupported","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"warning","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/platform-pagination.helper.ts","lineNumber":15,"sourceCode":"import type { AccountType } from '@yikart/common'\nimport type { ChannelPaginationInput, ChannelPaginationMetadata } from './platforms.interface'\nimport { AppException, ResponseCode } from '@yikart/common'\nimport { ChannelPaginationDirection, ChannelPaginationMode } from './platforms.interface'\n\nexport function normalizeChannelPagination(\n  platform: AccountType,\n  metadata: ChannelPaginationMetadata,\n  pagination: ChannelPaginationInput,\n): ChannelPaginationInput {\n  const normalized = getDefaultPagination(metadata, pagination)\n  const normalizedMode = getPaginationInputMode(normalized)\n\n  if (metadata.mode !== normalizedMode) {\n    throw new AppException(ResponseCode.ChannelPaginationModeNotSupported, {\n      platform,\n      expectedMode: metadata.mode,\n      actualMode: normalizedMode,\n    })\n  }\n\n  switch (normalizedMode) {\n    case ChannelPaginationMode.Cursor: {\n      const cursorMetadata = metadata as Extract<ChannelPaginationMetadata, { mode: ChannelPaginationMode.Cursor }>\n      const limit = normalized.limit ?? cursorMetadata.defaultLimit\n      if (limit > cursorMetadata.maxLimit) {\n        throw new AppException(ResponseCode.ChannelPaginationLimitExceeded, {\n          platform,\n          limit,\n          maxLimit: cursorMetadata.maxLimit,\n        })\n      }\n      const direction = normalized.direction ?? ChannelPaginationDirection.Next","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/platform-pagination.helper.ts#L1-L33","documentation":"ChannelPaginationModeNotSupported is thrown by normalizeChannelPagination when the pagination mode implied by the caller's input does not match the platform's declared ChannelPaginationMetadata.mode. Each platform declares cursor- or offset-based pagination; passing input that normalizes to the other mode is rejected.","triggerScenarios":"Calling normalizeChannelPagination (directly or via a platform listing call) with pagination input whose mode (from getPaginationInputMode, e.g. cursor vs page/offset fields) differs from metadata.mode — e.g. sending { cursor } to a platform that declares offset pagination.","commonSituations":"Generic channel listing UI sending the same pagination shape to all platforms; platform metadata changed its declared mode; caller mixes cursor and offset fields in one request.","solutions":["Inspect the platform's pagination metadata and send matching fields (cursor for Cursor mode, page/limit for offset)","Fix generic callers to branch on metadata.mode before building pagination input","Use the provider's own default pagination (getDefaultPagination) as the shape template"],"exampleFix":"// before\nnormalizeChannelPagination(platform, meta, { cursor: 'abc' }) // meta.mode = Offset -> throws\n// after\nnormalizeChannelPagination(platform, meta, { page: 1, limit: 20 }) // matches Offset mode","handlingStrategy":"type-guard","validationCode":"function matchesPaginationMode(input: Record<string, unknown>, mode: ChannelPaginationMode): boolean {\n  const hasCursor = typeof input.cursor === 'string'\n  const hasOffset = typeof input.page === 'number' || typeof input.offset === 'number'\n  return mode === ChannelPaginationMode.Cursor ? hasCursor : hasOffset\n}\nif (!matchesPaginationMode(input, metadata.mode)) throw new Error('Pagination mode mismatch')","typeGuard":"function isCursorPagination(p: unknown): p is { cursor: string; limit?: number } {\n  return typeof p === 'object' && p !== null && typeof (p as any).cursor === 'string'\n}","tryCatchPattern":"try {\n  const page = await listChannelWorks(channelId, pagination)\n} catch (e) {\n  if (e instanceof AppException && e.code === ResponseCode.ChannelPaginationModeNotSupported) {\n    // rebuild pagination from e.context.expectedMode and retry\n  } else throw e\n}","preventionTips":["Read the platform's pagination metadata before building input","Never mix cursor and page/offset fields in one request","Centralize pagination construction in a helper keyed on metadata.mode"],"tags":["pagination","input-validation","api-misuse"],"backgroundTag":"pagination-mode-mismatch","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}