Skip to content

sync: true on Percy-on-Automate fails on @percy/cli 1.32.0-1.32.2 with "upload is not async iterable" (works on 1.31.8-1.31.14) #2311

Description

@gbuh

Title

Regression in @percy/cli@1.32.0 - 1.32.2: sync Automate snapshots fail with {"error":"upload is not async iterable"} (works in 1.31.8 - 131.14)

The problem

After upgrading @percy/cli from 1.31.8 to 1.32.2, synchronous Percy-on-Automate snapshots fail.
Our Java Selenium tests call:

new Percy(driver).screenshot(name, options) // with sync: true

(sync: true is required for inline comparison validation.)

Instead of comparison results, the SDK returns:

{"error":"upload is not async iterable"}

Example snapshot: “View All default layout”.

On 1.31.8, the same flow works: sync returns full comparison JSON with "status":"success", dashboard URLs, and diff-info.
Screenshot capture on BrowserStack Automate succeeds in both cases; failure occurs in the CLI sync upload path after capture.

We believe this is a regression in sync handling of /percy/automateScreenshot introduced while fixing Maestro sync (commit 545d498 in PR #2217). The code uses for await (const _ of upload) on percy.upload(), but percy.upload is wrapped in percy.js to return a Promise via generatePromise(). The raw async generator appears to be percy.yield.upload(). Iterating a Promise causes this exact error.

Working (1.31.8):

const snapshotPromise = new Promise((resolve, reject) =>
  percy.upload(comparisonData, { resolve, reject }, 'automate'));

Broken (1.32.2):

const upload = percy.upload(comparisonData, { resolve, reject }, 'automate');
for await (const _ of upload) { /* drain */ }  // upload is a Promise, not async iterable

The same for await pattern appears in sync branches of /percy/comparison and /percy/maestro-screenshot in 1.32.2.

Workaround: Pin @percy/cli@1.31.8.

Suggested fix: Drain percy.yield.upload() instead of percy.upload(), or revert sync Automate routes to the 1.31.8 Promise pattern.


Environment

  • Node version: 14.21.3 (CodeBuild; npm warns image-size@1.2.1 wants Node >=16)
  • @percy/cli version: 1.32.2 (broken), 1.31.8 (works)
  • Percy SDK version: io.percy:percy-java-selenium:2.1.1
  • Build/snapshot reference: https://percy.io/76a47c69/web/LPPortal-Desktop2-eb42b0fd/builds/51116938
    (1.31.8 build with visual diff on “View All default layout”; sync path succeeded)
  • OS version: Linux (Amazon Linux 2, CodeBuild 4.14.355-282.733.amzn2.x86_64)
  • Shell/CLI: bash (npx percy exec --verbose -- ./gradlew test -Pvisual ...)

Details

Setup:

  • Percy-on-Automate via BrowserStack (-Premote=browserstack)
  • Run command:
    npx percy exec --verbose -- ./gradlew test -Pvisual -Penv=qa -Pbrowser=Chrome ...
  • Java test uses sync screenshots:
    Map<String, Object> options = new HashMap<>();
    options.put("fullPage", true);
    options.put("sync", true);
    JSONObject json = new Percy(getDriver()).screenshot(snapshotName, options);

Observed on 1.32.2:

  • Output for snapshot “View All default layout”:
    {"error":"upload is not async iterable"}
  • Validation fails because response has no status: success / screenshot payload.

Observed on 1.31.8 (same test/snapshot):

  • Full sync comparison JSON returned with "status":"success".
  • Percy CLI logs show expected flow:
    • Snapshot taken
    • Creating snapshot
    • Waiting for snapshot completion
    • Polling comparison status

Scope:

  • Affects sync Automate snapshots (sync: true)
  • Likely affects sync paths on /percy/comparison and /percy/maestro-screenshot too (same pattern)
  • Non-sync snapshots may still work (not fully verified)

References:


Debug logs

1.31.8 (working) excerpt:

+ npm install --save-dev @percy/cli@1.31.8
+ @percy/cli@1.31.8
[percy:webdriver-utils:captureScreenshot] [View All default layout] : Comparison Data: {...}
[percy:core] Snapshot taken: View All default layout
[percy:client] Creating snapshot: View All default layout...
[percy:core] Waiting for snapshot 'View All default layout' to be completed
[percy:core:wait-for-job] Polling for comparison status in 5000ms
Json object for snapshot 'View All default layout':
{"snapshot-name":"View All default layout",...,"status":"success",...}

1.32.2 (broken) excerpt:

Json object for snapshot 'View All default layout':
{"error":"upload is not async iterable"}

(Full CodeBuild logs available on request; Percy CLI verbose logging enabled.)


Code to reproduce issue

Minimal reproduction concept:

  1. Run visual tests on BrowserStack Automate with percy exec.
  2. Use io.percy:percy-java-selenium >= 2.0.4 with sync: true.
  3. Install @percy/cli@1.32.2 → sync snapshots return:
    {"error":"upload is not async iterable"}
  4. Pin @percy/cli@1.31.8 → same tests return valid sync comparison JSON.

Java test pattern:

Map<String, Object> options = new HashMap<>();
options.put("fullPage", true);
options.put("sync", true);
JSONObject json = new Percy(getDriver()).screenshot("View All default layout", options);
// 1.32.2: {"error":"upload is not async iterable"}
// 1.31.8: includes status, screenshots, dashboard-urls, diff-info

CI install line that reproduces:

npm install --save-dev @percy/cli@1.32.2
npx percy exec --verbose -- <your-test-runner-with-sync-percy-screenshots>

We do not yet have a minimal public repro repo; happy to provide additional logs or a stripped-down example if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions