{"record":{"id":"162b6a5e23947754","repo":"yikart/AiToEarn","slug":"userid-accountid-text","errorCode":null,"errorMessage":"userId, accountId和text是必须的","messagePattern":"userId, accountId和text是必须的","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts","lineNumber":203,"sourceCode":"        // userId: { type: 'string', description: '用户ID' },\n        accountId: { type: 'string', description: 'Twitter账号ID' },\n        text: { type: 'string', description: '推文内容' },\n        mediaIds: { type: 'array', items: { type: 'string' }, description: '媒体ID列表（可选）' }\n      },\n      required: ['userId', 'accountId', 'text']\n    }\n  })\n  @HttpCode(201)\n  async createTweet(\n    @GetToken() systemToken: TokenInfo,\n    // @Body('userId') userId: string,\n    @Body('accountId') accountId: string,\n    @Body('text') text: string,\n    @Body('mediaIds') mediaIds?: string[],\n  ) {\n    const userId = systemToken.id;\n    if (!userId || !accountId || !text) {\n      throw new BadRequestException('userId, accountId和text是必须的');\n    }\n\n    const accessToken = await this.twitterAuthService.getUserAccessToken(accountId);\n    return this.twitterService.createTweet(accessToken, userId, accountId, text, mediaIds);\n  }\n\n  /**\n   * 上传媒体文件\n   */\n  @Post('media/upload')\n  @ApiOperation({ summary: '上传媒体文件' })\n  @ApiConsumes('multipart/form-data')\n  @ApiBody({\n    schema: {\n      type: 'object',\n      properties: {\n        userId: { type: 'string' },\n        accountId: { type: 'string' },","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.controller.ts#L185-L221","documentation":"POST /plat/twitter/tweets/create publishes a tweet. userId comes from the authenticated system JWT while accountId, text, and optional mediaIds come from the JSON body. When userId, accountId, or text is missing/empty the controller throws BadRequestException('userId, accountId和text是必须的'). Note empty string '' is falsy, so blank tweet text also triggers this error.","triggerScenarios":"Unauthenticated/expired request (no systemToken.id); body missing accountId or text; text sent as empty string; body not sent as JSON so @Body() fields are all undefined; calling with mediaIds-only content and no text.","commonSituations":"Composer UI allowing an empty post; file uploads not accepted by the text-only endpoint (use /media/upload separately); automation scripts with malformed JSON or missing Content-Type: application/json; expired session JWT.","solutions":["Ensure the request carries a valid JWT and JSON body {\"accountId\":\"...\",\"text\":\"...\"} with Content-Type: application/json","Validate text is a non-empty string client-side before calling (trim and length-check)","If attaching media, upload first via POST /plat/twitter/media/upload then pass mediaIds along with text","Re-login if the JWT expired so userId resolves"],"exampleFix":"// before\nawait api.post('/plat/twitter/tweets/create', { accountId, text: content.trim() ? content : undefined });\n// after\nif (!content.trim()) throw new Error('Tweet text is required');\nawait api.post('/plat/twitter/tweets/create', { accountId, text: content, mediaIds }, { headers: { 'Content-Type': 'application/json' } });","handlingStrategy":"validation","validationCode":"const text = (content ?? '').trim();\nif (!systemToken || !accountId || !text) {\n  throw new Error('Tweet requires an authenticated user, accountId and non-empty text');\n}\nawait api.post('/plat/twitter/tweets/create', { accountId, text, mediaIds });","typeGuard":"function isCreateTweetBody(b: unknown): b is { accountId: string; text: string; mediaIds?: string[] } {\n  const x = b as any;\n  return !!x && typeof x.accountId === 'string' && x.accountId.length > 0\n    && typeof x.text === 'string' && x.text.trim().length > 0\n    && (x.mediaIds === undefined || (Array.isArray(x.mediaIds) && x.mediaIds.every(i => typeof i === 'string')));\n}","tryCatchPattern":"try {\n  return await api.post('/plat/twitter/tweets/create', body);\n} catch (e) {\n  if (e.response?.status === 400) {\n    console.error('createTweet rejected: ensure valid JWT and a JSON body with accountId and non-empty text');\n  }\n  throw e;\n}","preventionTips":["Block empty posts in the composer UI before dispatching","Always send Content-Type: application/json with the body","Upload media via /media/upload first and pass mediaIds with text — text cannot be empty even for media-only posts","Refresh expired JWTs so systemToken.id always resolves"],"tags":["validation","bad-request","request-body","twitter"],"backgroundTag":"missing-required-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}