jackwener/OpenCLI · error · CommandExecutionError

Twitter post completion returned a malformed status url.

Error message

Twitter post completion returned a malformed status url.

What it means

validateSubmitStatusPair parses the returned status url with new URL(). If the url is not a string, or fails to parse as an absolute URL (the try/catch around `new URL(result.url)`), this error is thrown. It ensures the CLI only reports shareable, well-formed tweet links.

Source

Thrown at clis/twitter/post.js:67

    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) {
    return requirePostActionResult(await page.evaluate(`(() => {
        const visible = (el) => !!el && (el.offsetParent !== null || el.getClientRects().length > 0);
        const boxes = Array.from(document.querySelectorAll('[data-testid="tweetTextarea_0"]'));

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Make the in-page script return the absolute URL (location.href) rather than a path
  2. Resolve a relative URL against the origin before returning: new URL(path, location.origin).href
  3. Check whether the post redirected to a login/captcha page and re-authenticate the browser session

Example fix

// before (in-page script)
return { ok: true, id, url: location.pathname };
// after
return { ok: true, id, url: location.origin + location.pathname };
Defensive patterns

Strategy: validation

Validate before calling

let parsed; try { parsed = new URL(result.url); } catch { throw new Error('status url must be an absolute http(s) URL'); }
if (!['https:', 'http:'].includes(parsed.protocol)) throw new Error('status url must be http(s)');

Type guard

function isAbsoluteHttpUrl(v) { if (typeof v !== 'string') return false; try { const u = new URL(v); return u.protocol === 'https:' || u.protocol === 'http:'; } catch { return false; } }

Try / catch

try {
  const result = submitTweet(page, text);
} catch (e) {
  if (String(e.message).includes('malformed status url')) { /* url was relative or unparseable; capture location.href instead */ }
  throw e;
}

Prevention

When it happens

Trigger: clickResult returns { ok: true, id, url } where url is a relative path like '/user/status/123', an empty string, or contains characters that make new URL() throw (unencoded spaces/bad escapes) — since the constructor runs in Node without a base, relative URLs fail.

Common situations: In-page script capturing location.pathname instead of location.href; X redirecting to a login or interstitial URL that still gets captured; idn/punycode or malformed redirect targets after posting.

Understand the failure class

Related errors


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