Skip to content

browser run: response.text() returns comma-joined bytes, breaking response.json() #354

Description

@ankitranjan7

Summary

Inside the browser run QuickJS sandbox, response.text() returns the body's bytes joined by commas instead of the decoded text. response.json() is therefore broken for every network response.

Repro

webcmd --session <id> browser run --stdin --no-snapshot-diff <<'JS'
const pending = page.waitForResponse(r => r.url().includes('/products/1'));
await page.goto('https://dummyjson.com/products/1');
const response = await pending;
const t = await response.text();
return { type: typeof t, first80: String(t).slice(0, 80), len: String(t).length };
JS

Actual:

{
  "type": "string",
  "first80": "123,34,105,100,34,58,49,44,34,116,105,116,108,101,34,58,34,69,115,115,101,110,99",
  "len": 5329
}

123, 34, 105, 100 are the byte values of {, ", i, d. Expected {"id":1,"title":"Essence....

Cause

src/browser/run/playwright-client/vendor/client/network.ts:732

async text(): Promise<string> {
  const content = await this.body();   // a plain Uint8Array under QuickJS, not a Node Buffer
  return content.toString('utf8');     // Uint8Array.toString() ignores the argument
}

Uint8Array.prototype.toString inherits Array.prototype.toString, which comma-joins the elements and discards the 'utf8' argument. There is no Node Buffer in the sandbox.

Knock-on effect

response.json() calls JSON.parse on that comma-joined string and fails with unexpected data at the end. normalizeExecutionError then matches /syntaxerror/i and reports it as BROWSER_RUN_SYNTAX_ERROR — telling the caller their program has a syntax error when it is valid JavaScript. Filed separately as #355.

Fix

The sandbox already injects a decoder for exactly this: decodeText in src/browser/run/playwright-client/quickjs-platform.ts, backed by __webcmdDecodeText in src/browser/run/runner.ts. this._platform is available on the class — see network.ts:393 for an existing use.

async text(): Promise<string> {
  const content = await this.body();
  return this._platform.decodeText(content);
}

The vendored client is bundled into src/browser/run/generated/playwright-client.js, so the bundle needs regenerating (see src/browser/run/playwright-client/README.md), and src/browser/run/playwright-client-build.test.ts may need updating.

Worth a regression test asserting response.text() returns decoded text for a known body.

Impact

In the internal task eval this cost roughly five wasted attempts in one scenario: the agent tried response.json(), then JSON.parse on the raw value, then several exploratory scripts, before working out that it had to decode comma-separated byte values by hand.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions