{"record":{"id":"2c414b0c05133042","repo":"yikart/AiToEarn","slug":"twitter-post-id-missing","errorCode":null,"errorMessage":"Twitter post id missing","messagePattern":"Twitter post id missing","errorType":"exception","errorClass":"TwitterPlatformException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/twitter/twitter.service.ts","lineNumber":241,"sourceCode":"      text: params.text,\n      ...(params.mediaIds?.length ? { media: { media_ids: params.mediaIds } } : {}),\n      ...(params.replyTo ? { reply: { in_reply_to_tweet_id: params.replyTo } } : {}),\n      ...(params.quoteTweetId ? { quote_tweet_id: params.quoteTweetId } : {}),\n      ...(params.replySettings ? { reply_settings: params.replySettings } : {}),\n      ...(params.poll ? { poll: params.poll } : {}),\n      made_with_ai: params.madeWithAi,\n      paid_partnership: params.paidPartnership,\n    }\n\n    const response = await this.runApiClientOperation<TwitterPostResponse>({\n      accessToken,\n      endpoint: 'POST /2/tweets',\n      context: { accountId: params.accountId },\n      call: client => client.posts.create(body),\n    })\n    const postId = response.data?.id\n    if (typeof postId !== 'string') {\n      throw new TwitterPlatformException('Twitter post id missing')\n    }\n\n    return {\n      postId,\n      permalink: `https://x.com/i/status/${postId}`,\n    }\n  }\n\n  async deletePost(accessToken: string, postId: string, accountId?: string): Promise<boolean> {\n    await this.runApiClientOperation({\n      accessToken,\n      endpoint: 'DELETE /2/tweets/:id',\n      context: { accountId, platformWorkId: postId },\n      call: client => client.posts.delete(postId),\n    })\n    return true\n  }\n","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/twitter/twitter.service.ts#L223-L259","documentation":"createPost submits a tweet via POST /2/tweets through a wrapped client call, then reads response.data?.id. If the platform response lacks a string id, TwitterPlatformException('Twitter post id missing') is thrown because the library cannot return a valid postId/permalink.","triggerScenarios":"POST /2/tweets returns 200 with an unexpected/empty body, the response wrapper nests the tweet under a different key, or the request actually failed silently (e.g. duplicate tweet, rate limit) while the wrapper still resolves.","commonSituations":"Duplicate content rejected silently by Twitter, Twitter API version change altering response shape, client wrapper swallowing errors and returning empty data.","solutions":["Log the full raw response of POST /2/tweets when id is missing to see platform errors","Check for twitter ApiResponseError handling in the client wrapper (raise non-2xx as platform errors)","Verify twitter-api-v2 client version matches the expected response shape (data.id)","Retry createPost only after confirming the tweet was not actually created (check for duplicates)"],"exampleFix":"// before\nconst postId = response.data?.id\nif (typeof postId !== 'string') {\n  throw new TwitterPlatformException('Twitter post id missing')\n}\n// after\nconst postId = response.data?.id\nif (typeof postId !== 'string') {\n  this.logger.error(`POST /2/tweets missing id, raw=${JSON.stringify(response)}`)\n  throw new TwitterPlatformException('Twitter post id missing')\n}","handlingStrategy":"type-guard","validationCode":"const body = { text: content.slice(0, 280) }\nif (!body.text) throw new Error('tweet text required before createPost')","typeGuard":"function hasPostId(r: unknown): r is { data: { id: string } } {\n  return !!r && typeof r === 'object' && typeof (r as any).data?.id === 'string'\n}","tryCatchPattern":"try {\n  const res = await twitterService.createPost(params)\n} catch (e) {\n  if (String(e?.message).includes('post id missing')) {\n    logger.error('tweet created but id missing; verify manually before retrying', { params })\n  }\n  throw e\n}","preventionTips":["Avoid blind retries after createPost — the tweet may exist without an id returned","Keep the twitter-api-v2 client version aligned with the response unwrapping logic","Log raw responses on failure","Deduplicate tweet content to avoid silent platform rejections"],"tags":["twitter","create-post","missing-response-field","api-response"],"backgroundTag":"missing-response-field","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}