{"record":{"id":"22e29deab9c97e2a","repo":"stablyai/orca","slug":"emulator-error-22e29d","errorCode":"emulator_error","errorMessage":"serve-sim AX returned invalid JSON.","messagePattern":"serve-sim AX returned invalid JSON\\.","errorType":"exception","errorClass":"EmulatorError","httpStatus":null,"severity":"error","filePath":"src/main/emulator/serve-sim-accessibility-tree.ts","lineNumber":27,"sourceCode":"  try {\n    const response = await net.fetch(axUrl, {\n      signal: AbortSignal.timeout(AX_REQUEST_TIMEOUT_MS)\n    })\n    const body = await response.text()\n    if (!response.ok) {\n      const detail = body.slice(0, MAX_ERROR_BODY_LENGTH) || response.statusText\n      const retry = response.status === 503 ? ' Accessibility may still be warming up; retry.' : ''\n      throw new EmulatorError(\n        'emulator_helper_failed',\n        `serve-sim AX request failed (${response.status}): ${detail}.${retry}`\n      )\n    }\n\n    let tree: unknown\n    try {\n      tree = JSON.parse(body)\n    } catch {\n      throw new EmulatorError('emulator_error', 'serve-sim AX returned invalid JSON.')\n    }\n    if (\n      !Array.isArray(tree) ||\n      tree.some((node) => typeof node !== 'object' || node === null || Array.isArray(node))\n    ) {\n      throw new EmulatorError('emulator_error', 'serve-sim AX returned an invalid tree.')\n    }\n    // serve-sim reports frames in absolute pixels; normalize to 0..1 so the\n    // output feeds straight back into tap/gesture.\n    return normalizeServeSimAxTree(tree)\n  } catch (error) {\n    if (error instanceof EmulatorError) {\n      throw error\n    }\n    const detail =\n      error instanceof Error && error.name === 'TimeoutError'\n        ? 'request timed out'\n        : error instanceof Error","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/emulator/serve-sim-accessibility-tree.ts#L9-L45","documentation":"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.","triggerScenarios":"serve-sim-accessibility-tree.ts:24-27 — response.ok is true but `JSON.parse(body)` throws.","commonSituations":"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.","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."],"exampleFix":"// before\nconst tree = await requestServeSimAccessibilityTree(axUrl) // invalid JSON\n\n// after\nlet tree\ntry { tree = await requestServeSimAccessibilityTree(axUrl) }\ncatch (e) { if (isEmulatorError(e, 'emulator_error')) await delay(500); tree = await requestServeSimAccessibilityTree(axUrl) }","handlingStrategy":"try-catch","validationCode":"const body = await (await fetch(axUrl)).text()\ntry { JSON.parse(body) } catch { /* serve-sim returned non-JSON; update/retry */ }","typeGuard":"function isEmulatorError(e: unknown, code = 'emulator_error'): e is import('./emulator-errors').EmulatorError {\n  return e instanceof Error && (e as any).code === code && e.name === 'EmulatorError'\n}","tryCatchPattern":"try { return await requestServeSimAccessibilityTree(axUrl) }\ncatch (e) {\n  if (isEmulatorError(e, 'emulator_error') && /invalid JSON/.test(e.message)) { await delay(500); return await requestServeSimAccessibilityTree(axUrl) }\n  throw e\n}","preventionTips":["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."],"tags":["ios","accessibility","json","serve-sim"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}