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
- Send only playlistId OR playlistItemsIds, never both.
- Strip empty defaults in the request builder so only the intended param is serialized.
- Make two calls if both a playlist listing and specific items are required.
- 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
- Remove always-on default playlistId from wrappers/SDK options.
- Treat the two lookups as separate API methods to make mixing impossible.
- Validate params at the call site before invoking the client.
- Log the final query string during development.
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
- 只能选择一个参数: id, myRating
- 只能选择一个参数: channelId, playListIds, mine
- No response from Gemini
- video duration is required
- DashScope HappyHorse does not support image_tail
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/da88080a29fc9e12.
Report an issue: GitHub.