stablyai/orca · error · EmulatorError
emulator_error
emulator_error
Error message
serve-sim AX returned invalid JSON.
What it means
The /ax endpoint returned 2xx but the body was not valid JSON, so JSON.parse threw. The accessibility tree contract is JSON; a non-JSON 2xx body means the response is corrupted, partial, or came from the wrong handler.
Source
Thrown at src/main/emulator/serve-sim-accessibility-tree.ts:27
try {
const response = await net.fetch(axUrl, {
signal: AbortSignal.timeout(AX_REQUEST_TIMEOUT_MS)
})
const body = await response.text()
if (!response.ok) {
const detail = body.slice(0, MAX_ERROR_BODY_LENGTH) || response.statusText
const retry = response.status === 503 ? ' Accessibility may still be warming up; retry.' : ''
throw new EmulatorError(
'emulator_helper_failed',
`serve-sim AX request failed (${response.status}): ${detail}.${retry}`
)
}
let tree: unknown
try {
tree = JSON.parse(body)
} catch {
throw new EmulatorError('emulator_error', 'serve-sim AX returned invalid JSON.')
}
if (
!Array.isArray(tree) ||
tree.some((node) => typeof node !== 'object' || node === null || Array.isArray(node))
) {
throw new EmulatorError('emulator_error', 'serve-sim AX returned an invalid tree.')
}
// serve-sim reports frames in absolute pixels; normalize to 0..1 so the
// output feeds straight back into tap/gesture.
return normalizeServeSimAxTree(tree)
} catch (error) {
if (error instanceof EmulatorError) {
throw error
}
const detail =
error instanceof Error && error.name === 'TimeoutError'
? 'request timed out'
: error instanceof ErrorView on GitHub (pinned to 1136503c6a)
Solutions
- Retry once — transient truncation can clear on the next request.
- Update serve-sim to a build matching the current Orca version.
- curl the /ax URL directly to inspect what serve-sim actually returned.
Example fix
// before
const tree = await requestServeSimAccessibilityTree(axUrl) // invalid JSON
// after
let tree
try { tree = await requestServeSimAccessibilityTree(axUrl) }
catch (e) { if (isEmulatorError(e, 'emulator_error')) await delay(500); tree = await requestServeSimAccessibilityTree(axUrl) } Defensive patterns
Strategy: try-catch
Validate before calling
const body = await (await fetch(axUrl)).text()
try { JSON.parse(body) } catch { /* serve-sim returned non-JSON; update/retry */ } Type guard
function isEmulatorError(e: unknown, code = 'emulator_error'): e is import('./emulator-errors').EmulatorError {
return e instanceof Error && (e as any).code === code && e.name === 'EmulatorError'
} Try / catch
try { return await requestServeSimAccessibilityTree(axUrl) }
catch (e) {
if (isEmulatorError(e, 'emulator_error') && /invalid JSON/.test(e.message)) { await delay(500); return await requestServeSimAccessibilityTree(axUrl) }
throw e
} Prevention
- Keep serve-sim version aligned with the Orca build.
- Retry once on invalid JSON (transient truncation).
- curl /ax directly when debugging to see the raw body.
When it happens
Trigger: serve-sim-accessibility-tree.ts:24-27 — response.ok is true but `JSON.parse(body)` throws.
Common situations: serve-sim bug returning an HTML/text error page with a 200 status; the response body was truncated by a dropped connection mid-stream; a serve-sim version mismatch returning a different content type.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- emulator_helper_failed
- emulator_helper_failed
- emulator_no_active
- ${commandLabel} ${args.join(' ')} ok=false: ${JSON.stringify
- Failed to load ${path.basename(jsonPath)}: ${error.message}
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/22e29deab9c97e2a.
Report an issue: GitHub.