Skip to content

[Web Beta] ClassCastException: LinkedHashMap cannot be cast to String after tap triggers navigation #2944

@mtford90

Description

@mtford90

Description

When running Maestro web tests, tapping a button that triggers page navigation (e.g., login form submission) causes a ClassCastException. The tap action completes successfully (form submits, loading spinner appears), but Maestro crashes before proceeding to the next command.

Error Message

[ERROR] maestro.orchestra.Orchestra.executeCommands: [Command execution] CommandFailed: class java.util.LinkedHashMap cannot be cast to class java.lang.String (java.util.LinkedHashMap and java.lang.String are in module java.base of loader 'bootstrap')

Steps to Reproduce

  1. Create a simple web login flow:
url: http://localhost:3000/login
---
- launchApp
- assertVisible: "Enter email"
- tapOn: "Enter email"
- inputText: "test@example.com"
- tapOn: "Enter Password"
- inputText: "password123"
- tapOn: "Log In"
- assertVisible: "Dashboard"  # Never reached
  1. Run: maestro test -p web login.yaml

  2. The test fills the form and clicks "Log In", but crashes immediately after the tap triggers navigation.

Expected Behavior

Maestro should wait for the navigation to complete and then proceed to the next assertion.

Actual Behavior

  • The tap executes successfully (login form submits, loading spinner visible in screenshots)
  • Browser closes immediately
  • Test fails with ClassCastException

Environment

  • Maestro Version: 2.1.0
  • Platform: macOS (Apple Silicon)
  • Java Version: OpenJDK 17.0.18
  • Browser: Chromium (launched by Maestro)
  • App: React SPA with Firebase Auth, uses client-side routing

Analysis

After investigating the source code, the issue appears to be in YamlFluentCommand.kt in the toElementSelector() method. During/after navigation:

  1. WebDriver.kt calls window.maestro.getContentDescription() to read DOM state
  2. During SPA navigation, this returns unexpected/malformed data
  3. Jackson deserializes it as LinkedHashMap instead of typed objects
  4. The selector parsing code lacks defensive type checking and crashes when casting

The WebDriver.kt waits for document.readyState === "complete", but this doesn't account for:

  • SPA client-side routing (React Router, etc.)
  • Async auth flows (Firebase, Auth0, etc.)
  • DOM changes during async operations

Workarounds Attempted (None Worked)

  • waitToSettleTimeoutMs: 0 on tapOn
  • retryTapIfNoChange: true
  • Using pressKey: Enter instead of tap
  • Running with --headless flag
  • Adding extendedWaitUntil after tap

Additional Context

  • The same login flow works perfectly with agent-browser (Playwright-based)
  • Mobile drivers (iOS/Android) handle navigation differently and may not have this issue
  • This appears to be web-beta specific

Debug Output

Screenshots show the form filled correctly with loading spinner visible, confirming the tap worked but Maestro crashed during navigation handling.

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