jackwener/OpenCLI · error · CommandExecutionError
Twitter post completion returned only one of id/url.
Error message
Twitter post completion returned only one of id/url.
What it means
After submitTweet clicks the post button, validateSubmitStatusPair checks the completion result. A successful tweet must supply the new status id and its url together; supplying exactly one of them means the browser script returned a partially-formed completion, so this error is thrown. It protects callers from using a tweet id without a shareable url (or vice versa).
Source
Thrown at clis/twitter/post.js:60
}
function requirePostActionResult(value, context) {
const result = unwrapBrowserResult(value);
if (!result || typeof result !== 'object' || Array.isArray(result) || typeof result.ok !== 'boolean') {
throw new CommandExecutionError(`${context} returned a malformed result.`);
}
if (Object.prototype.hasOwnProperty.call(result, 'message') && result.message != null && typeof result.message !== 'string') {
throw new CommandExecutionError(`${context} returned a malformed message.`);
}
if (Object.prototype.hasOwnProperty.call(result, 'error') && result.error != null && typeof result.error !== 'string') {
throw new CommandExecutionError(`${context} returned a malformed error.`);
}
return result;
}
function validateSubmitStatusPair(result) {
if ((result.id && !result.url) || (!result.id && result.url)) {
throw new CommandExecutionError('Twitter post completion returned only one of id/url.');
}
if (!result.id && !result.url) return;
if (typeof result.id !== 'string' || !/^\d+$/.test(result.id)) {
throw new CommandExecutionError('Twitter post completion returned a malformed status id.');
}
if (typeof result.url !== 'string') {
throw new CommandExecutionError('Twitter post completion returned a malformed status url.');
}
let url;
try {
url = new URL(result.url);
} catch {
throw new CommandExecutionError('Twitter post completion returned a malformed status url.');
}
const hostname = url.hostname.toLowerCase().replace(/^www\./, '');
const match = url.pathname.match(/^\/([^/]+)\/status\/(\d+)\/?$/);
if (!['x.com', 'twitter.com', 'mobile.twitter.com'].includes(hostname) || !match || match[2] !== result.id) {
throw new CommandExecutionError('Twitter post completion returned a malformed status url.');View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the post — a transient partial result often resolves once the composer page fully redirects
- Update post.js's clickResult in-page script to derive both id and url from the same source (parse the id out of the returned URL)
- Log the full result object in clickResult to see which of id/url was captured
- Pin/verify the CLI version matches the current X compose flow
Example fix
// before (in-page script): returns id scraped separately
return { ok: true, id: statusId };
// after: derive both from the redirected URL
const url = location.href;
const m = url.match(/\/status\/(\d+)/);
if (!m) return { ok: false, error: 'no status url after posting' };
return { ok: true, id: m[1], url }; Defensive patterns
Strategy: validation
Validate before calling
function submitPairComplete(r) { const hasId = !!(r && r.id), hasUrl = !!(r && r.url); return hasId === hasUrl; }
if (!submitPairComplete(clickResult)) throw new Error('posting returned incomplete result; retry'); Type guard
function isCompleteSubmitResult(v) { return typeof v === 'object' && v !== null && ('id' in v) === ('url' in v); } Try / catch
try {
const result = submitTweet(page, text);
} catch (e) {
if (String(e.message).includes('only one of id/url')) { /* retry once; likely partial page load */ }
throw e;
} Prevention
- Derive both id and url from the same redirected URL in in-page scripts
- Wait for the /status/<id> redirect before capturing the result
- Retry once on partial results before surfacing the failure
When it happens
Trigger: submitTweet's clickResult evaluate returns { ok: true, id: '123' } without url, or { ok: true, url: '...' } without id — e.g. the script scraped the URL from the address bar but failed to extract the status id from the DOM, or a redirect completed but the id regex failed.
Common situations: X UI changes moving the data-testid or anchor that carried the status id; the compose page redirecting to a URL the script parses but whose id extraction step throws silently; partial page loads right after clicking Post where only the URL is available yet.
Related errors
- Twitter post completion returned a malformed status id.
- ${context} returned a malformed result.
- ${context} returned a malformed message.
- ${context} returned a malformed error.
- Twitter post completion returned a malformed status url.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5d5934c2a6f1d7dd.
Report an issue: GitHub.