yikart/AiToEarn · error · TwitterPlatformException

Twitter post id missing

Error message

Twitter post id missing

What it means

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.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/twitter/twitter.service.ts:241

      text: params.text,
      ...(params.mediaIds?.length ? { media: { media_ids: params.mediaIds } } : {}),
      ...(params.replyTo ? { reply: { in_reply_to_tweet_id: params.replyTo } } : {}),
      ...(params.quoteTweetId ? { quote_tweet_id: params.quoteTweetId } : {}),
      ...(params.replySettings ? { reply_settings: params.replySettings } : {}),
      ...(params.poll ? { poll: params.poll } : {}),
      made_with_ai: params.madeWithAi,
      paid_partnership: params.paidPartnership,
    }

    const response = await this.runApiClientOperation<TwitterPostResponse>({
      accessToken,
      endpoint: 'POST /2/tweets',
      context: { accountId: params.accountId },
      call: client => client.posts.create(body),
    })
    const postId = response.data?.id
    if (typeof postId !== 'string') {
      throw new TwitterPlatformException('Twitter post id missing')
    }

    return {
      postId,
      permalink: `https://x.com/i/status/${postId}`,
    }
  }

  async deletePost(accessToken: string, postId: string, accountId?: string): Promise<boolean> {
    await this.runApiClientOperation({
      accessToken,
      endpoint: 'DELETE /2/tweets/:id',
      context: { accountId, platformWorkId: postId },
      call: client => client.posts.delete(postId),
    })
    return true
  }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Log the full raw response of POST /2/tweets when id is missing to see platform errors
  2. Check for twitter ApiResponseError handling in the client wrapper (raise non-2xx as platform errors)
  3. Verify twitter-api-v2 client version matches the expected response shape (data.id)
  4. Retry createPost only after confirming the tweet was not actually created (check for duplicates)

Example fix

// before
const postId = response.data?.id
if (typeof postId !== 'string') {
  throw new TwitterPlatformException('Twitter post id missing')
}
// after
const postId = response.data?.id
if (typeof postId !== 'string') {
  this.logger.error(`POST /2/tweets missing id, raw=${JSON.stringify(response)}`)
  throw new TwitterPlatformException('Twitter post id missing')
}
Defensive patterns

Strategy: type-guard

Validate before calling

const body = { text: content.slice(0, 280) }
if (!body.text) throw new Error('tweet text required before createPost')

Type guard

function hasPostId(r: unknown): r is { data: { id: string } } {
  return !!r && typeof r === 'object' && typeof (r as any).data?.id === 'string'
}

Try / catch

try {
  const res = await twitterService.createPost(params)
} catch (e) {
  if (String(e?.message).includes('post id missing')) {
    logger.error('tweet created but id missing; verify manually before retrying', { params })
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: Duplicate content rejected silently by Twitter, Twitter API version change altering response shape, client wrapper swallowing errors and returning empty data.

Related errors


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