stablyai/orca · error · EmulatorError

emulator_error

emulator_error

Error message

Cannot parse empty uiautomator XML

What it means

Thrown by parseUiAutomatorXml when the input string is empty or whitespace-only. It is the first guard before attempting to parse the XML document, distinguishing 'no data' from 'malformed data'. An empty dump usually means `adb shell uiautomator dump` produced no output — the command ran but wrote nothing.

Source

Thrown at src/main/emulator/android/uiautomator-tree.ts:41

export function parseAndroidBounds(value: string): AndroidAxBounds | null {
  const match = value.trim().match(/^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/)
  if (!match) {
    return null
  }
  return {
    left: Number(match[1]),
    top: Number(match[2]),
    right: Number(match[3]),
    bottom: Number(match[4])
  }
}

// Parses uiautomator dump XML. The returned node is the synthetic root that
// holds the top-level <node> children of <hierarchy>. Throws
// EmulatorError('emulator_error', ...) on unparseable input.
export function parseUiAutomatorXml(xml: string): AndroidAxNode {
  if (xml.trim() === '') {
    throw new EmulatorError('emulator_error', 'Cannot parse empty uiautomator XML')
  }
  let root: RawElement
  try {
    root = parseDocument(xml)
  } catch (error) {
    if (error instanceof EmulatorError) {
      throw error
    }
    throw new EmulatorError(
      'emulator_error',
      `Failed to parse uiautomator XML: ${(error as Error).message}`
    )
  }
  // A bare <node> root is treated as the single top-level node; otherwise take
  // the <node> children of <hierarchy>.
  const topLevel =
    root.tag === 'node' ? [root] : root.children.filter((child) => child.tag === 'node')
  return { children: topLevel.map(mapNode) }

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Wait for the device UI to be ready (e.g. sys.boot_completed AND a launcher activity) before dumping.
  2. Re-run the uiautomator dump and inspect raw stdout length before parsing; retry with backoff if empty.
  3. Run `adb shell uiautomator dump /sdcard/x.xml && adb shell cat /sdcard/x.xml` manually to see what the device emits.
  4. If the dump is legitimately empty (no views), handle the empty case upstream rather than feeding it to the parser.

Example fix

// before: parsing whatever dump returns
const tree = parseUiAutomatorXml(dump.stdout)
// after: retry on empty until UI is ready
if (dump.stdout.trim() === '') { await sleep(500); reDump() }
const tree = parseUiAutomatorXml(dump.stdout)
Defensive patterns

Strategy: validation

Validate before calling

// Skip parsing when the dump is empty; re-dump instead.
function hasDump(xml: string): boolean { return xml.trim() !== '' }

Try / catch

if (!hasDump(xml)) { await sleep(500); xml = await redump() }
if (hasDump(xml)) return parseUiAutomatorXml(xml)
throw new Error('uiautomator dump empty after retry')

Prevention

When it happens

Trigger: parseUiAutomatorXml('') or parseUiAutomatorXml(' \n') called with the result of a uiautomator dump that returned an empty string. This precedes the parseDocument call, so it short-circuits before any XML parsing.

Common situations: The uiautomator dump command failed silently (exit 0, empty stdout) on a device where the window manager is not ready; the dump file read failed; the device booted but the UI is not yet rendered; a headless window with no views; an outdated uiautomator binary returning nothing.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/ef57d89dfb8da360. Report an issue: GitHub.