Skip to content

[BUG][Windows] Bot gateway E2E browser check crashes on cp1252 when Playwright stderr contains box-drawing characters #3424

Description

@Dhivya-Bharathy

[BUG][Windows] Bot gateway E2E browser check crashes on cp1252 when Playwright stderr contains box-drawing characters

Metadata

Field Value
Repository https://github.com/MervinPraison/PraisonAI
Labels bug, windows, praisonai-bot, browser, developer-experience, e2e
Severity Medium — browser validation path broken on default Windows terminals; masks real Playwright install error
Component e2e-platform-audit/bot_gateway_e2e.py, Playwright subprocess capture
Affected platforms Windows 10/11 with cp1252 stdout (default PowerShell)
Live audit result 11 PASS / 3 FAIL (docker env, browser x2)

Executive summary

The live bot gateway E2E harness (e2e-platform-audit/bot_gateway_e2e.py --live-only) validates praisonai-bot, MCP, code execution, sandbox, and browser control end-to-end. On Windows with default cp1252 console encoding, the browser control checks fail with a secondary UnicodeEncodeError on box-drawing character (U+2551) instead of surfacing the actionable root cause: Playwright browser binaries are not installed (playwright install).

The failure chain is:

  1. _playwright_headless_probe() subprocess returns Playwright's framed install hint (uses box-drawing borders)
  2. The probe check records this in Report.detail via str(exc)[:200]
  3. Report.add() calls bare print() on the detail string (line 63)
  4. cp1252 stdout cannot encode → outer UnicodeEncodeError replaces the Playwright message
  5. Developer sees 'charmap' codec can't encode character '\u2551' instead of playwright install

11 of 14 live checks pass (gateway, MCP, code, sandbox). Docker failure is environment-dependent (daemon not running). Browser failure is a double bug: missing Playwright browsers plus encoding-unsafe error reporting.


Environment

Field Value
OS Windows 10 (10.0.19045)
Shell PowerShell (legacy conhost)
Python 3.13.2 (E2E runner); gateway subprocess may use 3.11
Console encoding cp1252
OPENAI_API_KEY Present (required for live tests)
PRAISONAI_AUTO_APPROVE true
Playwright Python package Installed
Playwright browser binaries Not installed (playwright install not run)
Docker daemon Not reachable (separate env failure)
Package versions (audit) praisonai-bot 0.0.41, praisonai-browser 0.0.8

Step-by-step reproduction

# 1. Clone repo
git clone https://github.com/MervinPraison/PraisonAI.git
cd PraisonAI

# 2. Install packages (editable monorepo or pip install -e ".[all]")
pip install -e ".[all]"
pip install websockets pytest pytest-asyncio

# 3. Ensure Playwright browsers are NOT installed (default fresh env)
#    Skip: playwright install chromium

# 4. Set API key and auto-approve
$env:OPENAI_API_KEY = "<your-key>"
$env:PRAISONAI_AUTO_APPROVE = "true"

# 5. Do NOT force UTF-8 — reproduce cp1252
Remove-Item Env:PYTHONUTF8 -ErrorAction SilentlyContinue

# 6. Run live-only E2E
python e2e-platform-audit/bot_gateway_e2e.py --live-only

Expected behavior

  • Browser checks FAIL with clear message: Please run: playwright install
  • Other live checks pass (gateway, MCP, code, sandbox)
  • E2E harness completes without crashing its own stdout

Actual behavior

  • Gateway, MCP, code, sandbox checks PASS
  • browser control (playwright headless) FAIL — detail truncated but contains playwright install and chars (visible in JSON report)
  • browser control via bot message FAIL — detail shows encoding error, not Playwright hint
  • Summary: === Done: 3 failed === (docker + browser x2)

Full terminal evidence

Live E2E console output (sanitized paths)

ERROR:    Traceback (most recent call last):
  File "<venv>/site-packages/starlette/routing.py", line 645, in lifespan
    await receive()
  ...
asyncio.exceptions.CancelledError

=== PraisonAI Bot Gateway E2E ===
Repo: <repo-root>
Logs: <repo-root>/e2e-platform-audit/logs/bot-gateway-e2e

[OK] import praisonai_bot — 0.0.41
[OK] import praisonai_code — 0.0.56
[OK] import praisonai_browser — 0.0.8
[OK] import praisonai_mcp — 0.0.8
[OK] import praisonai_sandbox — 0.0.6
[OK] OPENAI_API_KEY — present
[FAIL] docker sandbox live execute — docker daemon not reachable
[OK] gateway startup (praisonai-bot) — 127.0.0.1:18765
[OK] mcp gateway /mcp endpoint — agent tool listed (1 tools)
[OK] mcp tools/call via bot gateway — response len=10
[OK] praisonai-code execute via bot message — marker found
[OK] sandbox exec via bot message — marker found
[FAIL] browser control via bot message — 'charmap' codec can't encode character '\u2551' in position 62: character maps to <undefined>

=== Done: 3 failed ===
Report: <repo-root>/e2e-platform-audit/logs/bot-gateway-e2e/report_20260727_130141.json

JSON report excerpt (shows masked Playwright message)

The browser control (playwright headless) check captured the real error before the outer handler failed:

{
  "name": "browser control (playwright headless)",
  "status": "FAIL",
  "detail": "updated.       \u2551\r\n\u2551 Please run the following command to download new browsers: \u2551\r\n\u2551                                                            \u2551\r\n\u2551     playwright install                              ",
  "duration_s": 10.3
},
{
  "name": "browser control via bot message",
  "status": "FAIL",
  "detail": "'charmap' codec can't encode character '\\u2551' in position 62: character maps to <undefined>",
  "duration_s": 10.3
}

The JSON file is UTF-8 and preserves the Playwright hint; the console loses it due to cp1252 print().


Interactive analysis

Symptom → diagnosis → fix flowchart

flowchart TD
    A[bot_gateway_e2e.py --live-only] --> B[browser control check]
    B --> C[_playwright_headless_probe subprocess]
    C --> D{Browsers installed?}
    D -->|No| E[Playwright stderr with U+2551 box chars]
    D -->|Yes| F[Browser probe PASS]
    E --> G[str exc captured in Report.detail]
    G --> H[Report.add calls print detail]
    H --> I{stdout encoding cp1252?}
    I -->|Yes| J[UnicodeEncodeError on U+2551]
    I -->|No UTF-8| K[Playwright hint visible in console]
    J --> L[Outer except: charmap error shown]
    L --> M[Developer misdiagnoses as encoding bug]
    K --> N[Developer runs playwright install]
    J --> O[Fix: safe_print + ASCII sanitize subprocess output]
    O --> P[Console shows playwright install hint]
Loading

Failure sequence diagram

sequenceDiagram
    participant E2E as bot_gateway_e2e.py
    participant PW as Playwright subprocess
    participant R as Report.add / print()
    participant Out as stdout cp1252

    E2E->>PW: chromium.launch headless probe
    PW-->>E2E: stderr with ║ framed "playwright install"
    E2E->>E2E: Check FAIL detail = str(exc)[:200]
    E2E->>R: add(FAIL, detail with ║)
    R->>Out: print(f"[FAIL] ... {detail}")
    Out-->>R: UnicodeEncodeError U+2551
    R-->>E2E: exception in outer browser block
    E2E->>R: add(FAIL, "charmap codec...")
    Note over E2E: Real fix (playwright install) hidden from console
Loading

Root cause analysis

Layer 1: Missing Playwright browsers (environment)

_playwright_headless_probe() at e2e-platform-audit/bot_gateway_e2e.py lines 385–408 runs:

proc = await asyncio.create_subprocess_exec(
    sys.executable, "-c", script,
    stdout=asyncio.subprocess.PIPE,
    stderr=asyncio.subprocess.PIPE,
    env=os.environ.copy(),
)
...
if proc.returncode != 0:
    err = (stderr or b"").decode(errors="replace").strip()
    raise RuntimeError(err[-400:] if err else f"exit {proc.returncode}")

When Chromium is not installed, Playwright emits a multi-line banner using box-drawing characters ( U+2551). This is correctly captured in RuntimeError message.

Layer 2: Encoding-unsafe console reporting (bug)

Report.add() at lines 60–63:

def add(self, check: Check) -> None:
    self.checks.append(check)
    icon = {"PASS": "OK", "FAIL": "FAIL", "SKIP": "SKIP"}.get(check.status, "?")
    print(f"[{icon}] {check.name}" + (f" — {check.detail}" if check.detail else ""))

Bare print() encodes check.detail to cp1252 stdout. When detail contains , Python raises UnicodeEncodeError.

Layer 3: Outer exception handler masks root cause

Browser block lines 571–640:

except Exception as exc:
    report.add(Check("browser control via bot message", "FAIL", str(exc)[:200], time.time() - t0))

If report.add() itself raises (or a prior add in the same block fails), the outer handler records the encoding error, not the Playwright install hint.

Note on PYTHONUTF8 in harness

The script sets os.environ.setdefault("PYTHONUTF8", "1") at line 30, but this does not reliably reconfigure an already-initialized stdout on all Windows Python builds. Audit was run with cp1252-active console; failure reproduced.


Impact assessment

Check Result Notes
Package imports PASS All five packages
Gateway startup PASS 127.0.0.1:18765
MCP /mcp endpoint PASS Agent tool listed
MCP tools/call PASS Live LLM round-trip
praisonai-code via WS PASS E2E_CODE_OK marker
Sandbox via WS PASS E2E_SANDBOX_OK marker
Docker sandbox FAIL Env: daemon not running
Browser Playwright probe FAIL Browsers not installed
Browser via bot message FAIL Encoding masks Playwright hint

Severity justification: Medium

  • Blocks Windows developers from diagnosing browser setup
  • Does not affect Linux/macOS CI with UTF-8
  • Real fix for users is playwright install, but they cannot discover it from console output

Proposed fixes

Fix 1: Encoding-safe Report.add() (required)

Before (e2e-platform-audit/bot_gateway_e2e.py, lines 60–63):

def add(self, check: Check) -> None:
    self.checks.append(check)
    icon = {"PASS": "OK", "FAIL": "FAIL", "SKIP": "SKIP"}.get(check.status, "?")
    print(f"[{icon}] {check.name}" + (f" — {check.detail}" if check.detail else ""))

After:

def _safe_console(text: str) -> str:
    import sys
    enc = getattr(sys.stdout, "encoding", None) or "utf-8"
    try:
        text.encode(enc)
        return text
    except UnicodeEncodeError:
        return text.encode(enc, errors="replace").decode(enc)

def add(self, check: Check) -> None:
    self.checks.append(check)
    icon = {"PASS": "OK", "FAIL": "FAIL", "SKIP": "SKIP"}.get(check.status, "?")
    line = f"[{icon}] {check.name}"
    if check.detail:
        line += f" — {_safe_console(check.detail)}"
    print(line)

Fix 2: Sanitize subprocess stderr before storing in Check.detail

In _playwright_headless_probe() after decoding stderr:

import re

def _ascii_sanitize(text: str) -> str:
    # Strip box-drawing and other non-ASCII for console-safe reports
    return re.sub(r"[^\x20-\x7E\r\n\t]", "?", text)

err = _ascii_sanitize((stderr or b"").decode(errors="replace").strip())
if "playwright install" in err.lower():
    raise RuntimeError(
        "Playwright browsers not installed. Run: playwright install chromium"
    )
raise RuntimeError(err[-400:] if err else f"exit {proc.returncode}")

Fix 3: Pre-flight browser check with ASCII-only message

Add early check before live gateway tests:

def _check_playwright_browsers(report: Report) -> bool:
    try:
        asyncio.run(_playwright_headless_probe())
        return True
    except RuntimeError as exc:
        msg = str(exc)
        if "playwright install" in msg.lower() or "download" in msg.lower():
            report.add(Check(
                "playwright browsers installed",
                "FAIL",
                "Run: playwright install chromium",
            ))
        else:
            report.add(Check("playwright browsers installed", "FAIL", _safe_console(msg)[:200]))
        return False

Skip browser bot checks when pre-flight fails — avoids cascading encoding errors.

Fix 4: Wrap report.add in try/except (defensive)

def add(self, check: Check) -> None:
    self.checks.append(check)
    try:
        self._emit(check)
    except UnicodeEncodeError:
        check = Check(check.name, check.status, "<detail omitted: non-ASCII>", check.duration_s)
        self._emit(check)

Workarounds

Workaround 1: Install Playwright browsers (fixes root environment issue)

playwright install chromium
python e2e-platform-audit/bot_gateway_e2e.py --live-only

Workaround 2: Force UTF-8 console

chcp 65001
$env:PYTHONUTF8 = "1"
$env:PYTHONIOENCODING = "utf-8"
python e2e-platform-audit/bot_gateway_e2e.py --live-only

Workaround 3: Read JSON report instead of console

python e2e-platform-audit/bot_gateway_e2e.py --live-only
Get-Content e2e-platform-audit/logs/bot-gateway-e2e/report_*.json -Encoding utf8 |
  ConvertFrom-Json | Select-Object -ExpandProperty checks |
  Where-Object { $_.name -like "*browser*" }

The JSON report preserves the Playwright install message even when console fails.


Test plan

Manual (Windows cp1252, browsers not installed)

  • E2E completes without uncaught exception
  • Console shows playwright install or ASCII equivalent — not charmap codec
  • JSON report unchanged (still UTF-8 full detail)

Manual (Windows cp1252, browsers installed)

  • browser control (playwright headless) PASS
  • browser bot agent via WS message PASS or fails with LLM-related reason only

Unit test for safe console

def test_report_add_cp1252_safe(monkeypatch, capsys):
    monkeypatch.setattr(sys.stdout, "encoding", "cp1252")
    report = Report()
    report.add(Check("browser", "FAIL", "Please run playwright install \u2551"))
    out = capsys.readouterr().out
    assert "UnicodeEncodeError" not in out
    assert "playwright install" in out.lower()

CI recommendation

- name: Bot gateway E2E live (Windows)
  if: runner.os == 'Windows'
  run: |
    playwright install chromium
    python e2e-platform-audit/bot_gateway_e2e.py --live-only
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
    PRAISONAI_AUTO_APPROVE: "true"

Acceptance criteria

  • bot_gateway_e2e.py --live-only never raises UnicodeEncodeError from Report.add()
  • When Playwright browsers missing, console shows actionable playwright install hint in ASCII
  • JSON report continues to store full UTF-8 detail
  • No regression on UTF-8 terminals (Linux/macOS/Windows Terminal)
  • Documentation mentions playwright install chromium as prerequisite for browser E2E

Related files

Path Role
e2e-platform-audit/bot_gateway_e2e.py Primary fix locationReport.add, _playwright_headless_probe
src/praisonai-bot/praisonai_bot/cli/features/bots_cli.py Browser tool wiring (BrowserBaseTool, line ~980)
src/praisonai-browser/ Browser package doctor / install docs
e2e-platform-audit/logs/bot-gateway-e2e/report_*.json UTF-8 evidence preserved

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingclaudeAuto-trigger Claude analysisdocumentationImprovements or additions to documentation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions