Billionmail/BillionMail · error

playwright capture failed: %w output: %s

Error message

playwright capture failed: %w
output: %s

What it means

CaptureScreenshots runs a generated Node.js Playwright script via `node -e` and captures combined stdout/stderr. If the process exits non-zero, the Go error is wrapped with 'playwright capture failed: %w' plus the script's output so the Playwright-side failure is visible.

Source

Thrown at core/internal/service/video_gen/screenshots.go:75

	}

	result := &ScreenshotResult{
		Homepage: filepath.Join(cfg.OutputDir, "homepage.png"),
		Contact:  filepath.Join(cfg.OutputDir, "contact.png"),
		Google:   filepath.Join(cfg.OutputDir, "google.png"),
	}

	// Build the Playwright script that captures all 3 screenshots
	script := buildPlaywrightScript(cfg, result)

	timeoutCtx, cancel := context.WithTimeout(ctx, cfg.Timeout)
	defer cancel()

	cmd := exec.CommandContext(timeoutCtx, "node", "-e", script)
	cmd.Env = append(os.Environ(), "PLAYWRIGHT_BROWSERS_PATH=0")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("playwright capture failed: %w\noutput: %s", err, string(out))
	}

	return result, nil
}

// buildPlaywrightScript generates a Node.js script that uses Playwright to
// capture the 3 screenshots. Exported for testing command construction.
func buildPlaywrightScript(cfg ScreenshotConfig, result *ScreenshotResult) string {
	contactURL := findContactURL(cfg.WebsiteURL)
	googleURL := buildGoogleMapsURL(cfg.BusinessName)

	return fmt.Sprintf(`
const { chromium } = require('playwright');
(async () => {
  const browser = await chromium.launch({ headless: true });
  const ctx = await browser.newContext({
    viewport: { width: %d, height: %d },
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Read the 'output:' portion of the error — it contains the actual Playwright/Node stack trace
  2. Run `npx playwright install chromium` in the deployment image if browsers are missing
  3. Re-run with a longer timeout or retry for slow/unresponsive target sites
  4. Test the target URLs manually; blocklisted/bot-protected sites need a different capture approach

Example fix

// before
out, err := cmd.CombinedOutput()
if err != nil {
	return nil, fmt.Errorf("playwright capture failed: %w\noutput: %s", err, string(out))
}
// after
out, err := cmd.CombinedOutput()
if err != nil {
	if errors.Is(ctx.Err(), context.DeadlineExceeded) {
		return nil, fmt.Errorf("playwright capture timed out after %v\noutput: %s", timeout, string(out))
	}
	return nil, fmt.Errorf("playwright capture failed: %w\noutput: %s", err, string(out))
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := exec.LookPath("node"); err != nil {
	return fmt.Errorf("node not installed")
}
for _, u := range []string{homepageURL, contactURL, googleURL} {
	if _, err := url.ParseRequestURI(u); err != nil {
		return fmt.Errorf("invalid capture URL %q", u)
	}
}

Try / catch

res, err := CaptureScreenshots(ctx, cfg)
if err != nil && strings.Contains(err.Error(), "playwright capture failed") {
	log.Printf("playwright output: %s", extractOutput(err)) // inspect Node-side trace before retrying
	if isTransient(err) { return retryCapture(ctx, cfg) }
}

Prevention

When it happens

Trigger: The embedded Playwright script fails: page.goto timeout, navigation error, browser not installed, target site blocks automation or returns 4xx/5xx, or ctx deadline cancels the command.

Common situations: Playwright browsers not installed (missing `npx playwright install`); PLAYWRIGHT_BROWSERS_PATH misconfigured; prospect's website down or blocking headless browsers; 60s timeout too short for slow sites.


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/25f157dcc9bdc56a. Report an issue: GitHub.