jackwener/OpenCLI · error · CommandExecutionError

Could not find an add-to-cart button on the product page.

Error message

Could not find an add-to-cart button on the product page.

What it means

CommandExecutionError thrown when the in-page script cannot find a clickable add-to-cart button: no button/a[role=button]/input[type=button] whose label matches 장바구니/카트/cart (and is not sold out / 품절). The library aborts rather than clicking blindly.

Source

Thrown at clis/coupang/add-to-cart.js:137

            throw new CommandExecutionError(`coupang add-to-cart navigation failed: ${error?.message || error}`);
        });
        const result = await page.evaluate(buildAddToCartEvaluate(productId)).catch((error) => {
            throw new CommandExecutionError(`coupang add-to-cart evaluation failed: ${error?.message || error}`);
        });
        const loginHints = result?.loginHints ?? {};
        if (loginHints.hasLoginLink && !loginHints.hasMyCoupang) {
            throw new AuthRequiredError('coupang.com', 'Please log into Coupang in Chrome and retry.');
        }
        const actualProductId = normalizeProductId(result?.currentProductId || productId);
        if (result?.reason === 'PRODUCT_MISMATCH') {
            const observed = actualProductId ? `got ${actualProductId}` : 'no product id observed';
            throw new CommandExecutionError(`Product mismatch: expected ${productId}, ${observed}`);
        }
        if (result?.reason === 'OPTION_REQUIRED') {
            throw new CommandExecutionError('This product requires option selection and is not supported in v1.');
        }
        if (result?.reason === 'ADD_TO_CART_BUTTON_NOT_FOUND') {
            throw new CommandExecutionError('Could not find an add-to-cart button on the product page.');
        }
        if (!result?.ok) {
            throw new CommandExecutionError('Failed to confirm add-to-cart success.');
        }
        return [{
                ok: true,
                product_id: actualProductId || productId,
                url: finalUrl,
                message: 'Added to cart',
            }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the product is in stock (sold-out items are intentionally not clicked)
  2. Add an explicit wait/delay so the page is fully rendered before add-to-cart runs
  3. Check the page in the controlled Chrome browser to see the actual button label/markup
  4. If Coupang changed its DOM, update the button-matching selectors in buildAddToCartEvaluate
  5. Ensure locale/IP serves the standard Korean Coupang layout

Example fix

// before
 coupang add-to-cart --product-id 12345  # sold out, button says 품절
// after
 # check availability first
 coupang product --product-id 12345   # confirm in-stock, then retry
Defensive patterns

Strategy: retry

Validate before calling

const text = await page.evaluate(() => document.body.innerText || '');
if (/품절|sold out/i.test(text)) {
  throw new Error(`Product sold out; add-to-cart will fail`);
}

Type guard

function hasAddToCartButton(buttons) {
  return buttons.some(b => /장바구니|카트|cart/i.test((b.innerText || '') + (b.getAttribute?.('aria-label') || '')) && !/품절|sold out/i.test(b.innerText || ''));
}

Try / catch

try {
  await addToCart(page, productId);
} catch (err) {
  if (/add-to-cart button/.test(err.message)) {
    await page.waitForTimeout(3000);   // let the page finish rendering
    await addToCart(page, productId);  // retry once
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Product page markup differs from expected selectors — sold-out items, out-of-stock buttons, page loaded in an unexpected language/layout, or Coupang DOM changes renaming cart buttons.

Common situations: Product is sold out (button says 품절 and is filtered out); page not fully rendered when evaluate ran; Coupang redesign changed button text/attributes; non-Korean locale serving different labels.

Related errors


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