yikart/AiToEarn · warning · BadRequestException

只能选择一个参数: playlistId, playlistItemsIds

Error message

只能选择一个参数: playlistId, playlistItemsIds

What it means

The getPlayItemsList endpoint requires exactly one lookup mode: `playlistId` (list items of a playlist) or `playlistItemsIds` (fetch specific playlist items). The controller filters out undefined/null/'' values and throws BadRequestException when more than one is present. Thrown locally before any YouTube API request or token refresh.

Source

Thrown at project/aitoearn-electron/server/src/modules/plat/youtube/youtube.controller.ts:488

  }

  @ApiOperation({ summary: '获取播放列表项' })
  // @Public()
  @Get('playlist/items/list')
  async getPlayItemsList(
    @GetToken() token: TokenInfo,
    @Query('accountId') accountId: string,
    @Query('playlistId') playlistId: string,
    @Query('id') playlistItemsIds: string,
    @Query('maxResults') maxResults?: string,
    @Query('pageToken') pageToken?: string,
    ) {
    // 校验确保只有一个参数传递
    const params = [playlistId, playlistItemsIds];
    const nonEmptyParams = params.filter(param => param !== undefined && param !== null && param !== '');

    if (nonEmptyParams.length > 1) {
      throw new BadRequestException('只能选择一个参数: playlistId, playlistItemsIds');
    }
    const accessToken = await this.youtubeAuthService.getUserAccessToken(accountId);
    return this.youtubeService.getPlayItemsList(accessToken, playlistId, playlistItemsIds, maxResults, pageToken);
  }

  @ApiOperation({ summary: '添加播放列表项' })
  @Post('playlist/items/insert')
  async PlayItemsInsert(
    @GetToken() token: TokenInfo,
    @Body('accountId') accountId: string,
    @Body('snippet') snippet?: Record<string, any>,
    @Body('contentDetails') contentDetails?: Record<string, any>,
  ) {
    const accessToken = await this.youtubeAuthService.getUserAccessToken(accountId);
    return this.youtubeService.insertPlayItems(accessToken, snippet, contentDetails);
  }

  @ApiOperation({ summary: '更新播放列表项' })

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Send only playlistId OR playlistItemsIds, never both.
  2. Strip empty defaults in the request builder so only the intended param is serialized.
  3. Make two calls if both a playlist listing and specific items are required.
  4. Log the outgoing query string to spot accidental always-on defaults.

Example fix

// before
getPlayItemsList(accountId, playlistId, itemIds, maxResults);
// after
getPlayItemsList(accountId, itemIds?.length ? undefined : playlistId, itemIds?.length ? itemIds : undefined, maxResults);
Defensive patterns

Strategy: validation

Validate before calling

if ((playlistId ? 1 : 0) + (playlistItemsIds?.length ? 1 : 0) !== 1) {
  throw new Error('Provide exactly one of playlistId or playlistItemsIds');
}

Type guard

function hasExactlyOne(a?: string, b?: string[]): boolean {
  return [a !== undefined && a !== '', Array.isArray(b) && b.length > 0].filter(Boolean).length === 1;
}

Try / catch

try {
  const items = await client.getPlayItemsList(accountId, playlistId, undefined, 50);
} catch (e) {
  if (String(e.message).includes('playlistId, playlistItemsIds')) console.warn('Mixed lookup modes; adjust caller');
  throw e;
}

Prevention

When it happens

Trigger: GET playlistItems with both ?playlistId=PL...&playlistItemsIds=PI1,PI2, or an SDK layer that always includes playlistId as a default while also passing item IDs.

Common situations: Wrappers with hardcoded default playlistId plus dynamic item IDs; UI forms that keep a previously selected playlist while switching to 'fetch by item ids' mode; batch scripts concatenating options.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/da88080a29fc9e12. Report an issue: GitHub.