ElizaOS Twitter plugin
not posting? Every known cause.
Your agent generates tweets but nothing shows up on X, or the logs are full of failed logins and your phone is full of security alerts. This page walks through each symptom, what actually causes it in the plugin, the fix where one exists, and an honest note where one does not.
7-day free trial. No X developer account needed.
ElizaOS Twitter plugin not working: the 60-second diagnostic
Almost every report of the plugin "not working" falls into one of four buckets. Match your logs to a row, then jump to that section.
Symptom 1: Eliza agent generates tweets but never publishes
This is the classic case from GitHub issue #1958: the agent composes a tweet, logs "Saving Tweet", and then... nothing. No "Posting" log line, no API call, no tweet. Issue #3693 is the same shape: the agent chats fine when you talk to it directly, but never posts autonomously. Generation and publishing are separate pipelines in Eliza, so one can succeed while the other silently does nothing.
Check these in order:
TWITTER_DRY_RUN is still true
Dry run mode generates and logs tweets without posting them. It is the single most common cause of "generates but never publishes". Set TWITTER_DRY_RUN=false explicitly, do not rely on a default.
Posting is not enabled
Autonomous posting is gated behind its own flag, TWITTER_POST_ENABLE=true. Without it the agent will happily generate content for the post loop that never runs.
You are inside the posting interval
The post loop waits a random time between TWITTER_POST_INTERVAL_MIN and TWITTER_POST_INTERVAL_MAX minutes. If those are large, it can look dead for hours. Set TWITTER_POST_IMMEDIATELY=true while testing so the agent posts on startup.
Your env vars never reach the runtime
Settings in the character file secrets block override the .env file, and a stale value there (for example dry run left on from testing) wins silently. Print the resolved settings at startup, or temporarily hardcode them, to confirm what the runtime actually sees.
The session died after startup
With the credential-login plugin versions, a login can succeed at boot and then be invalidated by X mid-run. The post step then fails without a loud error. If posting worked and stopped, read symptom 2.
A known-good testing config for the current plugin:
TWITTER_DRY_RUN=false
TWITTER_POST_ENABLE=true
TWITTER_POST_IMMEDIATELY=true
TWITTER_POST_INTERVAL_MIN=90
TWITTER_POST_INTERVAL_MAX=180If the agent posts with this config and stops when you restore yours, the problem was config. If it still does not post, it is auth: keep reading.
Symptom 2: "Failed to login to Twitter"
The older Twitter integration (client-twitter, and plugin versions built on the agent-twitter-client scraper) does not use the X API at all. It logs into x.com with your raw TWITTER_USERNAME, TWITTER_PASSWORD and TWITTER_EMAIL, exactly like a headless browser. That means every anti-bot defense X runs against suspicious logins runs against your agent. When X changes its login flow, the plugin breaks the same day, as in issue #5172:
Login attempt failed: Unknown subtask LoginUserPasskeyIdentifier
Twitter login failed after maximum retries.
Max retries reached. Exiting login process.What each variant means, and what helps:
Unknown subtask LoginUserPasskeyIdentifier
X inserted a passkey step into the login flow that the scraper does not know how to answer. There is no env var that fixes this. Sometimes disabling passkeys on the X account changes the flow enough to get past it, sometimes it does not.
Unknown subtask ArkoseLogin
X served an Arkose (FunCaptcha) anti-bot challenge. A headless login cannot solve it. Users report that dropping the VPN and logging in from a residential IP avoids the challenge, because datacenter IPs are scored as bots. That is a mitigation, not a fix.
2FA account, login always fails
If the account has two-factor auth, set TWITTER_2FA_SECRET to the TOTP secret (the string shown when you set up an authenticator app). Without it, credential login cannot complete the challenge.
Cookie login: "Session is invalid. Login required."
Reusing exported browser cookies (auth_token, ct0, guest_id) avoids fresh logins, but the format is finicky: the docs and code have disagreed on "name" vs "key" fields, and browser cookies are scoped to .x.com while parts of the stack expected .twitter.com. If cookies fail, re-export them and check the field names against your exact plugin version.
Cannot read properties of undefined (reading 'id_str')
Issue #4894, seen at startup on plugin 1.0.2: the Twitter service registers, tries to fetch the logged-in profile, gets nothing back, and crashes. It looks like a plugin bug, but the root cause is the same silent login failure.
Being honest here: these are not bugs you can patch around for long. Credential login is X's anti-bot surface, and it changes without notice. Every mitigation above has broken at least once, and the maintainers closed several of these issues as not planned. The library behind this approach has its own graveyard of the same errors, see our agent-twitter-client alternatives page.
Symptom 3: repeated X login security alerts (and eventual account locks)
If your inbox fills with "New login to your X account" alerts every time the agent runs, you are hitting issue #1969: the plugin performed a full fresh login every time the post action fired, instead of reusing the session the Twitter client already held. From X's side, that is a new device from a server IP logging into your account dozens of times a day. First it alerts, then it challenges, then it locks the account.
Makes it worse
- Running plugin-twitter and client-twitter together (issue #5172): two components fight over one session and re-login constantly. Pick one.
- Restart loops (crash, redeploy, dev reloads): every restart is a fresh credential login.
- Running from a datacenter or VPN IP, which raises X's suspicion score for each of those logins.
Reduces it
- Upgrade: newer versions cache cookies and reuse sessions instead of re-logging-in per post.
- Cookie-based auth so restarts resume a session instead of opening a new one.
- One Twitter component only, in one process only. No duplicate agents on the same account.
Even with all of that, the pattern of a bot holding a user session is exactly what X's security systems exist to catch. The alerts are the system working as designed. The only structural fix is to stop logging in with credentials at all: either the official X API (which the current @elizaos/plugin-twitter uses) or a posting API that holds proper OAuth tokens for you.
The two paths that actually stay fixed
The ElizaOS team reached the same conclusion: the current @elizaos/plugin-twitter dropped credential login for the official X API with OAuth 1.0a keys. That ends the login-challenge and security-alert class of failures. It also changes what you are signing up for.
Official X API via the plugin
- Requires an X developer account and app setup (OAuth 1.0a keys, not OAuth 2.0)
- Pay-per-use billing: roughly $0.015 per post, $0.20 per post containing a link
- You manage keys, tiers and rate limits yourself
- No scheduling: the agent posts when the loop fires, or not at all
OpenTweet API from a custom action
- No X developer account: connect X to OpenTweet with a normal login, once
- Account-safe OAuth held and refreshed server-side, zero credential logins
- Flat monthly price, no per-post fees (see pricing)
- Posting, scheduling, threads and batches through one endpoint
The pay-per-use numbers move; here is the current breakdown: X API pay-per-use explained.
Point your Eliza action at OpenTweet instead
Keep Eliza for what it is good at, generating content and deciding when to speak, and replace only the fragile posting layer. One custom action, one HTTP call, no login flow to break.
A minimal custom action that posts through OpenTweet:
import type { Action, IAgentRuntime, Memory, State, HandlerCallback } from '@elizaos/core';
export const postToX: Action = {
name: 'POST_TO_X',
description: 'Publish a tweet to X through the OpenTweet API',
similes: ['TWEET', 'PUBLISH_TWEET'],
validate: async (runtime: IAgentRuntime) => !!runtime.getSetting('OPENTWEET_API_KEY'),
handler: async (
runtime: IAgentRuntime,
message: Memory,
_state?: State,
_options?: Record<string, unknown>,
callback?: HandlerCallback
) => {
const res = await fetch('https://opentweet.io/api/v1/posts', {
method: 'POST',
headers: {
Authorization: `Bearer ${runtime.getSetting('OPENTWEET_API_KEY')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: message.content.text, publish_now: true }),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`OpenTweet API ${res.status}: ${err.error ?? 'request failed'}`);
}
await callback?.({ text: 'Posted to X.' });
return true;
},
examples: [],
};Want the agent to queue content instead of firing instantly? Send a timestamp:
{
"text": "Scheduled by my Eliza agent.",
"scheduled_date": "2026-07-15T15:00:00Z"
}Set OPENTWEET_API_KEY in your .env or character secrets, register the action, and delete the Twitter login config entirely. If your setup speaks MCP, you can skip the custom action and connect the hosted server at https://mcp.opentweet.io/mcp instead, with create_tweet, create_thread, schedule_tweet and about 33 more tools. Full setup details are on the ElizaOS integration page.
No credential login
OpenTweet holds real X OAuth tokens and refreshes them. Nothing for anti-bot systems to flag.
No login-flow breakage
Passkey subtasks and Arkose challenges are not your problem when nothing logs into x.com.
Post or schedule
publish_now for instant, scheduled_date to queue, batch endpoints for a week of content in one call.
Building agents beyond Eliza? The same key works from any AI agent, and the agents overview covers the full API and MCP surface.
Frequently asked questions
Why is my ElizaOS Twitter plugin not working at all?
Work through three checks in order. First, config: TWITTER_DRY_RUN must be false and TWITTER_POST_ENABLE must be true, and the values must actually reach the runtime (a character secrets block overrides your .env). Second, auth: if the logs show a login error like Unknown subtask LoginUserPasskeyIdentifier or ArkoseLogin, X is blocking the credential login itself. Third, conflicts: never enable plugin-twitter and client-twitter at the same time, they fight over the same session and both lose.
Why does my Eliza agent generate tweets but never publish them?
This is GitHub issue #1958. The generation pipeline and the publishing pipeline are separate, so the agent can happily log Saving Tweet while the post step silently does nothing. The usual culprits are TWITTER_DRY_RUN left on, TWITTER_POST_ENABLE unset, waiting inside a long TWITTER_POST_INTERVAL_MIN window without TWITTER_POST_IMMEDIATELY, or a login that quietly expired after startup.
How do I fix "Failed to login to Twitter" in ElizaOS?
If the error mentions Unknown subtask LoginUserPasskeyIdentifier or ArkoseLogin, X has served a login challenge the plugin cannot solve, and there is no config value that fixes it. Things that sometimes help: set TWITTER_2FA_SECRET to your TOTP secret if the account has 2FA, log in from a residential IP instead of a VPN or datacenter host, and reuse exported browser cookies instead of fresh credential logins. None of these are permanent fixes because the login flow is X's anti-bot surface and it changes without notice.
Why does X send me a security alert every time my Eliza agent posts?
GitHub issue #1969: the plugin performed a full fresh login on every post action instead of reusing the existing session. Every login from a server IP looks like a suspicious new device, so X alerts you each time and eventually challenges or locks the account. Newer plugin versions improved cookie reuse, but any architecture built on repeated credential logins keeps this risk.
Does the new @elizaos/plugin-twitter fix these problems?
Partly. The current plugin moved from credential login to the official X API with OAuth 1.0a keys, which ends the security-alert and login-challenge problems. The trade is that you now need an X developer account and pay-per-use X API billing, roughly $0.015 per post and $0.20 per post containing a link. An alternative is pointing your Eliza action at OpenTweet, which posts through account-safe OAuth with no developer account and a flat price.
Can my Eliza agent post to X without an X developer account?
Yes. Connect your X account to OpenTweet with a normal login, create an API key, and have your Eliza action send an HTTP POST to https://opentweet.io/api/v1/posts. OpenTweet holds the X OAuth tokens and handles refresh, so there is no developer application, no pay-per-use billing, and no credential login for X to flag. Scheduling and threads are supported through the same endpoint.
Stop debugging login flows
Connect X once, get one key, and let your Eliza agent post and schedule through an API that does not break when X changes its login page.
Start your 7-day free trial