jackwener/OpenCLI · error · CommandExecutionError
Tweet failed to post.
Error message
Tweet failed to post.
What it means
CommandExecutionError thrown when submitTweet explicitly reports failure ({ ok: false }) — unlike the unconfirmed timeout case, this is a definite failure and the message states nothing was posted, so retrying is safe.
Source
Thrown at clis/twitter/post.js:355
}
// Insert and verify the text after media upload so text + images are in
// the final Draft.js composer state immediately before clicking Post.
const typeResult = await insertComposerText(page, text);
if (!typeResult?.ok) {
throw new CommandExecutionError(typeResult?.message ?? 'Could not type tweet text.', 'Open the composer in the browser and check whether X is asking you to log in.');
}
await page.wait(1);
const result = await submitTweet(page, text);
if (result?.unconfirmed) {
// The poll expiring does not mean the tweet stayed in the composer,
// so this must not read as a definite failure: the agent workflow
// retries CommandExecutionError and would post twice (#2255).
throw new TimeoutError('twitter post', SUBMIT_TIMEOUT_MS / 1000, `${result.message} Check \`opencli twitter tweets --limit 1\` before retrying; the post may already be live.`);
}
if (!result?.ok) {
throw new CommandExecutionError(result?.message ?? 'Tweet failed to post.', 'Nothing was posted. Open the composer in the browser and retry.');
}
return [{
status: 'success',
message: result.message,
text,
...(result.id ? { id: result.id } : {}),
...(result.url ? { url: result.url } : {}),
}];
}
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Read result?.message for X's specific reason (e.g. duplicate status, rate limit)
- If duplicate content, edit the tweet text and retry
- If rate-limited, wait and retry later
- Open the composer in the browser and post manually to see X's on-page error
- Check the account isn't locked/limited; verify the composer flow still works after X deploys
Defensive patterns
Strategy: try-catch
Validate before calling
// avoid X duplicate rejection before posting
const recent = await opencli('twitter tweets', { limit: 1 });
if (recent?.[0]?.text === text) throw new Error('tweet text identical to last post');
if (!text.trim()) throw new Error('tweet text is empty'); Try / catch
try {
await postTweet({ text });
} catch (e) {
if (e.name === 'CommandExecutionError' && /failed to post/i.test(e.message)) {
console.error('Post definitively failed (nothing posted):', e.message);
// safe to fix the cause and retry
} else throw e;
} Prevention
- Change text before re-posting similar content (X blocks duplicates)
- Check account health/rate limits before automation runs
- Never treat this error as ambiguous — nothing was posted, so retrying is safe
- Read result.message for X's specific rejection reason
When it happens
Trigger: submitTweet resolves with result.ok falsy and no unconfirmed flag: the Post button click failed, X returned an error (duplicate post, empty tweet, rate limit, account restriction), or result is undefined/null.
Common situations: Tweeting text identical to a recent tweet (X duplicate detection); account temporarily limited from posting; X frontend changes breaking the submit flow; composer discarded the text.
Related errors
- 12306 tk auth cookie missing
- ${probe.detail}
- 12306 whoami failed: ${probe.detail}
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- Waiting for 12306 tk auth cookie
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/56b1fff835ba8c6b.
Report an issue: GitHub.