yikart/AiToEarn · warning · BadRequestException

只能选择一个参数: channelId, playListIds, mine

Error message

只能选择一个参数: channelId, playListIds, mine

What it means

The getPlayList endpoint accepts three mutually-exclusive modes: `channelId` (lists a channel's playlists), `playListIds` (fetch specific playlists), or `mine=true` (list the authenticated user's playlists). The controller counts non-empty params and throws BadRequestException if more than one is supplied, mirroring how the YouTube Data API requires a single filter. This is thrown before the Google API call.

Source

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

  }

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

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

  @ApiOperation({ summary: '更新播放列表' })
  @Post('playlist/update')
  async playlistUpdate(
    @GetToken() token: TokenInfo,
    @Body('accountId') accountId: string,
    @Body('playListId') playListId: string,
    @Body('snippet') snippet?: Record<string, any>,
    @Body('status') status?: Record<string, any>,
  ) {
    const accessToken = await this.youtubeAuthService.getUserAccessToken(accountId);
    return this.youtubeService.updatePlayList(accessToken, playListId, snippet, status);
  }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Pass exactly one of channelId, playListIds, mine per request; remove the others.
  2. Add client-side logic that clears the other fields when one mode is selected.
  3. Split needs into multiple calls (one per mode) and merge results in the client.
  4. Check the request URL/interceptor logs for stale default params like mine=true.

Example fix

// before
client.getPlayList(accountId, channelId, playListIds, true);
// after
client.getPlayList(accountId, playListIds ? undefined : channelId, playListIds, playListIds ? undefined : true);
Defensive patterns

Strategy: validation

Validate before calling

const modes = [channelId, playListIds, mine].filter(v => v !== undefined && v !== null && v !== '');
if (modes.length !== 1) throw new Error('Exactly one of channelId, playListIds, mine must be set');

Type guard

type PlaylistQuery = { channelId?: string } | { playListIds?: string[] } | { mine?: boolean };
function isSingleModeQuery(q: PlaylistQuery): boolean {
  return Object.values(q).filter(v => v !== undefined && v !== null && v !== '').length === 1;
}

Try / catch

try {
  return await client.getPlayList(accountId, undefined, undefined, true);
} catch (e) {
  if (String(e.message).includes('只能选择一个参数')) console.error('Playlist query mixed modes; fix caller');
  throw e;
}

Prevention

When it happens

Trigger: Calling the playlists endpoint with e.g. ?channelId=UC...&mine=true, or ?playListIds=PL1&mine=true — any combination of two or three of channelId/playListIds/mine that are non-empty (undefined/null/'' filtered out).

Common situations: Clients defaulting `mine` to a truthy value while also passing a channelId; bulk fetch code appending both playListIds and a channel filter; copy-pasted curl commands retaining params from a previous example.

Related errors


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