jackwener/OpenCLI · error · CommandExecutionError

Twitter post completion returned a malformed status id.

Error message

Twitter post completion returned a malformed status id.

What it means

When both id and url are present, validateSubmitStatusPair requires id to be a string of digits (regex /^\d+$/). Anything else — a number, undefined-coerced value, or string with non-numeric characters — throws this error. This guarantees the reported tweet id is a valid numeric Twitter status id suitable for constructing links.

Source

Thrown at clis/twitter/post.js:64

    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.');
    }
}

async function focusComposer(page) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Make the in-page script return the id as a raw digit string (e.g. from match[1] of /status\/(\d+)/)
  2. Strip any surrounding characters/whitespace from the id before returning it (id.trim().replace(/\D/g, ''))
  3. Verify the script isn't capturing a draft or client-side id rather than the server status id

Example fix

// before (in-page script)
return { ok: true, id: 1730000000000000000, url };
// after
return { ok: true, id: String(statusIdFromUrl), url };
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof id !== 'string' || !/^\d+$/.test(id)) throw new Error('status id must be a digit string');

Type guard

function isNumericIdString(v) { return typeof v === 'string' && /^\d+$/.test(v); }

Try / catch

try {
  await cli.run('twitter post', { text });
} catch (e) {
  if (String(e.message).includes('malformed status id')) { /* check captured id source in page script */ }
  throw e;
}

Prevention

When it happens

Trigger: submitTweet's clickResult script returns an id that is a JavaScript number instead of a string, or a string like 'status_123', a URL-encoded id, or a tweet draft/placeholder id that contains letters.

Common situations: In-page scripts doing `id: Number(statusId)` or reading id from an attribute containing extra characters; future X snowflake-like ids or local draft ids leaked into the completion result; locale formatting adding separators to the id.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/d1770d5d22615206. Report an issue: GitHub.