{"record":{"id":"4cc90ea97036edc3","repo":"yikart/AiToEarn","slug":"userid-accountid-file","errorCode":null,"errorMessage":"userId, accountId和file是必须的","messagePattern":"userId, accountId和file是必须的","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts","lineNumber":238,"sourceCode":"        userId: { type: 'string' },\n        accountId: { type: 'string' },\n        file: {\n          type: 'string',\n          format: 'binary',\n        },\n      },\n    },\n  })\n  @UseInterceptors(FileInterceptor('file'))\n  async uploadMedia(\n    @GetToken() systemToken: TokenInfo,\n    // @Body('userId') userId: string,\n    @Body('accountId') accountId: string,\n    @UploadedFile() file: Express.Multer.File,\n  ) {\n    const userId = systemToken.id;\n    if (!userId || !accountId || !file) {\n      throw new BadRequestException('userId, accountId和file是必须的');\n    }\n\n    const accessToken = await this.twitterAuthService.getUserAccessToken(accountId);\n    return this.twitterService.uploadMedia(accessToken, userId, accountId, file.buffer, file.mimetype);\n  }\n\n  /**\n   * 获取推文详情\n   */\n  @Get('tweets/detail')\n  @ApiOperation({ summary: '获取推文详情' })\n  @ApiQuery({ name: 'tweetId', type: 'string', description: '推文ID' })\n  // @ApiQuery({ name: 'userId', required: true, description: '用户ID' })\n  @ApiQuery({ name: 'accountId', required: true, description: 'Twitter账号ID' })\n  async getTweetDetail(\n    @GetToken() systemToken: TokenInfo,\n    @Query('tweetId') tweetId: string,\n    // @Query('userId') userId: string,","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts#L220-L256","documentation":"This NestJS BadRequestException is thrown by the Twitter uploadMedia controller endpoint when the request body is missing accountId or the multipart file (userId is taken from the system token, so it fails only if the token has no id). It is a guard clause before the Twitter media upload API is called, ensuring required inputs exist. The endpoint expects a multipart/form-data upload with an 'accountId' field and a 'file' part.","triggerScenarios":"POST to the uploadMedia endpoint with a body lacking 'accountId', or a request not sent as multipart/form-data so @UploadedFile() yields undefined, or a malformed/invalid system token whose id is falsy.","commonSituations":"Client sends JSON instead of multipart/form-data; file field name in FormData doesn't match the Multer interceptor's expected field; forgetting the accountId form field; calling the endpoint without a valid system token.","solutions":["Resend the request as multipart/form-data including both an 'accountId' field and the file part named as the Multer interceptor expects.","Verify the file field name in FormData matches the @UploadedFile() configuration in the controller.","Check that the request carries a valid system token that resolves to a non-empty systemToken.id.","Confirm the Multer file-upload interceptor (e.g. FileInterceptor) is actually applied to the route, otherwise file is always undefined."],"exampleFix":"// before (client)\nawait fetch(url, { method: 'POST', body: JSON.stringify({ accountId }) });\n// after (client)\nconst fd = new FormData();\nfd.append('accountId', accountId);\nfd.append('file', fileBlob, 'media.png');\nawait fetch(url, { method: 'POST', headers: authHeaders, body: fd });","handlingStrategy":"validation","validationCode":"if (!accountId || !file) throw new Error('accountId和file是必须的');\n// send as multipart/form-data with the file part","typeGuard":"function hasUploadParams(b: unknown): b is { accountId: string; file: File } {\n  return typeof b === 'object' && b !== null &&\n    typeof (b as any).accountId === 'string' && (b as any).accountId.length > 0 &&\n    (b as any).file instanceof File;\n}","tryCatchPattern":"try {\n  const res = await api.post('/twitter/media/upload', fd);\n} catch (e) {\n  if (e.response?.status === 400 && /必须/.test(e.response.data?.message)) {\n    // fix request shape: accountId + multipart file\n  }\n}","preventionTips":["Always use FormData for this endpoint; never JSON.","Match the file field name expected by the Multer interceptor.","Append accountId as a form field alongside the file.","Check auth token presence before calling token-protected endpoints."],"tags":["nestjs","validation","bad-request","missing-parameter","multipart"],"backgroundTag":"missing-required-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}