jackwener/OpenCLI · error · CommandExecutionError

xiaohongshu/unfollow: malformed current-url payload

Error message

xiaohongshu/unfollow: malformed current-url payload

What it means

After navigating to the profile URL and settling, the command reads location.href via page.evaluate and unwraps it with unwrapEvaluateResult. If the unwrapped value is not a string, the browser bridge returned something unexpected, so this CommandExecutionError is thrown to fail fast rather than call new URL() on garbage.

Source

Thrown at clis/xiaohongshu/unfollow.js:211

            required: true,
            positional: true,
            help: 'User ID (e.g. 5d8f88dc0000000001005d3a) or profile URL',
        },
    ],
    columns: ['status', 'user_id', 'url'],
    func: async (page, kwargs) => {
        if (!page) {
            throw new CommandExecutionError('Browser session required for xiaohongshu unfollow');
        }
        try {
            const userId = assertUserId(kwargs['user-id']);
            const url = `https://www.xiaohongshu.com/user/profile/${userId}`;
            await page.goto(url);
            await page.wait({ time: PROFILE_SETTLE_MS / 1000 });

            const hrefRaw = unwrapEvaluateResult(await page.evaluate('() => location.href'));
            if (typeof hrefRaw !== 'string') {
                throw new CommandExecutionError('xiaohongshu/unfollow: malformed current-url payload');
            }
            const parsedHref = new URL(hrefRaw);
            if (parsedHref.protocol !== 'https:' || !isXiaohongshuHost(parsedHref.hostname)) {
                throw new CommandExecutionError(
                    `xiaohongshu/unfollow: expected Xiaohongshu profile host, got ${parsedHref.hostname}`,
                );
            }
            if (/\/login(?:[/?#]|$)/i.test(parsedHref.pathname)) {
                throw new AuthRequiredError('www.xiaohongshu.com');
            }
            const currentProfile = parsedHref.pathname.match(/^\/user\/profile\/([a-zA-Z0-9]{8,32})\/?$/);
            if (currentProfile?.[1] !== userId) {
                throw new CommandExecutionError(
                    `xiaohongshu/unfollow: expected profile ${userId}, got ${parsedHref.pathname}`,
                );
            }

            // Step 1: click 已关注 (idempotent — bails out if 关注 is visible)

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the CLI/driver packages so page.evaluate and unwrapEvaluateResult use the same result-wrapping convention
  2. Confirm you are using the library's own page object/driver, not a custom evaluate wrapper that changes return shapes
  3. Re-run the command — a transient page crash or navigation race can produce a non-string result; also increase settle time if the page was still loading
  4. Inspect what page.evaluate('() => location.href') actually returns in your environment and adapt (it must be a plain string)

Example fix

// before
const href = await myWrapper.evaluate(page, '() => location.href'); // returns { value: '...' }
// after
const href = unwrapEvaluateResult(await page.evaluate('() => location.href')); // string
Defensive patterns

Strategy: try-catch

Type guard

function isString(v) { return typeof v === 'string'; }
// usage: const href = unwrapEvaluateResult(raw); if (!isString(href)) fallBackOrRetry();

Try / catch

try { await cli.unfollow({ 'user-id': id }); } catch (e) { if (String(e.message).includes('malformed current-url payload')) { /* retry once with the library's own driver, or upgrade library versions */ } else throw e; }

Prevention

When it happens

Trigger: page.evaluate returning a non-string payload — e.g. the wrapper returned {value: ...}, an array, undefined, or a serialized error object instead of a raw string; a custom/driver-specific evaluate that wraps results; a proxy or patched evaluate injecting metadata; navigation failing so the evaluate result is an error object.

Common situations: Using a different driver/automation layer whose evaluate result shape differs from what the CLI expects; library version mismatch where unwrapEvaluateResult conventions changed; page crashed or was closed mid-evaluate so the promise resolves with a non-string sentinel.

Understand the failure class

Related errors


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