jackwener/OpenCLI · error · CommandExecutionError
Could not type tweet text.
Error message
Could not type tweet text.
What it means
CommandExecutionError thrown when insertComposerText cannot type/verify the tweet text in the Draft.js composer. The hint tells the user to open the composer in the browser and check whether X is asking them to log in, because the most common cause is a lost session.
Source
Thrown at clis/twitter/post.js:343
if (!isRecoverableFileInputError(err)) {
throw err;
}
await attachImagesViaDataTransfer(page, absPaths);
}
} else {
await attachImagesViaDataTransfer(page, absPaths);
}
const uploadState = await waitForImageUpload(page, absPaths.length);
if (!uploadState?.ok) {
throw new TimeoutError('twitter image upload', UPLOAD_TIMEOUT_MS / 1000, 'Nothing was posted. Retry, or attach a smaller image.');
}
}
// 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 } : {}),View on GitHub (pinned to 49907e53dc)
Solutions
- Open the browser session and confirm you are logged into X (the error's own suggestion); re-authenticate if needed
- Retry once — a transient login wall often clears after a fresh page.goto
- Check whether X is showing an interstitial (verification, suspension, ToS notice) on the account
- If it persists after re-login, the composer DOM may have changed; update or report the insertComposerText selectors
Defensive patterns
Strategy: try-catch
Try / catch
try {
await postTweet({ text });
} catch (e) {
if (e.name === 'CommandExecutionError' && /type tweet text/i.test(e.message)) {
await relogin(); // session likely expired
return postTweet({ text });
}
throw e;
} Prevention
- Refresh the X session (opencli login) before long automation runs
- Detect login walls early by checking x.com/home before posting
- Keep text non-empty and free of characters that break Draft.js insertion
- Handle one transient retry, then surface the error for human review
When it happens
Trigger: insertComposerText(page, text) returns { ok: false } — the textarea never accepts input or the typed text is not reflected in the composer state — typically because X rendered a login wall or the editor selector changed.
Common situations: Expired X session cookies mid-run; X showing an age/account-verification interstitial; rate limiting that soft-blocks posting; the composer DOM changing after an X frontend deploy.
Related errors
- BUTTON_NOT_FOUND: Follow button not on profile page (logged
- Favorites button not found - make sure you are logged in
- BUTTON_NOT_FOUND
- Like button not found - make sure you are logged in
- Not logged into x.com (no ct0 cookie)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/feb0a971fa621a7f.
Report an issue: GitHub.