{"record":{"id":"292fc17deba8f6f3","repo":"stablyai/orca","slug":"emulator-helper-failed-292fc1","errorCode":"emulator_helper_failed","errorMessage":"Could not download scrcpy server: ${detail}","messagePattern":"Could not download scrcpy server: (.+?)","errorType":"exception","errorClass":"EmulatorError","httpStatus":null,"severity":"error","filePath":"src/main/emulator/android/scrcpy-server-download.ts","lineNumber":57,"sourceCode":"  }\n  if (!inFlightDownload) {\n    inFlightDownload = downloadScrcpyServerJar(path).finally(() => {\n      inFlightDownload = null\n    })\n  }\n  return inFlightDownload\n}\n\nasync function downloadScrcpyServerJar(path: string): Promise<string> {\n  emulatorProbe('scrcpy.jar.download.start', { url: DOWNLOAD_URL, dest: path })\n  mkdirSync(dirname(path), { recursive: true })\n  try {\n    await downloadTo(DOWNLOAD_URL, path)\n  } catch (error) {\n    rmSync(path, { force: true })\n    emulatorProbeError('scrcpy.jar.download.fail', error, { url: DOWNLOAD_URL })\n    const detail = error instanceof Error ? error.message : 'unknown error'\n    throw new EmulatorError('emulator_helper_failed', `Could not download scrcpy server: ${detail}`)\n  }\n  if (!isScrcpyServerJarReady()) {\n    rmSync(path, { force: true })\n    throw new EmulatorError(\n      'emulator_helper_failed',\n      'Downloaded scrcpy server was invalid or truncated.'\n    )\n  }\n  emulatorProbe('scrcpy.jar.download.ok', { dest: path, bytes: statSync(path).size })\n  return path\n}\n\nfunction downloadTo(url: string, dest: string, redirects = 0): Promise<void> {\n  return new Promise((resolve, reject) => {\n    if (redirects > 5) {\n      reject(new Error('too many redirects'))\n      return\n    }","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/emulator/android/scrcpy-server-download.ts#L39-L75","documentation":"Thrown by downloadScrcpyServerJar when downloadTo(DOWNLOAD_URL, path) rejects. The catch block cleans up the partial file (rmSync), records a probe error, extracts the underlying message, and re-throws as EmulatorError('emulator_helper_failed'). The scrcpy server jar is fetched on demand to mirror the device's scrcpy version.","triggerScenarios":"First scrcpy session on a host with no cached jar; downloadTo's HTTP request fails (DNS, connection refused, timeout, HTTP >= 400, or >5 redirects). The inFlightDownload de-duplication means only one download is attempted at a time, so concurrent failures share one throw.","commonSituations":"Offline or firewalled host blocking the scrcpy release URL; corporate proxy intercepting the download; transient network blip; DNS resolution failure; a redirect loop (>5 hops); disk full so the write stream errors mid-download.","solutions":["Check connectivity to the scrcpy release URL (curl -I) and allow it through any proxy/firewall.","Pre-download the jar and place it at the expected localJarPath so the runtime fetch is skipped.","Retry — inFlightDownload is cleared on failure so the next session re-attempts.","For proxies, ensure HTTPS_PROXY/HTTP_PROXY env vars are set so downloadTo routes correctly.","Free disk space at the destination if the write stream is failing."],"exampleFix":"// before: relying on runtime fetch behind a firewall\nawait ScrcpyStreamSession.start({ runner, sdk, serial, localJarPath: cachedOrDownloaded }, cb)\n// after: pre-place the jar (or allow the URL)\ncp /mnt/cache/scrcpy-server.jar localJarPath\n// or: allow the release host through the proxy\nexport HTTPS_PROXY=http://corp-proxy:3128","handlingStrategy":"retry","validationCode":"// Probe reachability before the runtime fetch.\nimport { request } from 'node:https'\nfunction reachable(url: string): Promise<boolean> {\n  return new Promise((res) => {\n    const req = request(url, { method: 'HEAD' }, (r) => res(r.statusCode === 200))\n    req.on('error', () => res(false))\n    req.end()\n  })\n}","typeGuard":"import { EmulatorError } from '../emulator-errors'\nfunction isScrcpyDownloadError(e: unknown): e is EmulatorError {\n  return e instanceof EmulatorError && e.code === 'emulator_helper_failed' && /download scrcpy/i.test(e.message)\n}","tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  try { return await ScrcpyStreamSession.start(opts, cb) }\n  catch (e) {\n    if (isScrcpyDownloadError(e) && attempt < 2) { await sleep(1000 * (attempt + 1)); continue }\n    throw e\n  }\n}","preventionTips":["Pre-stage the scrcpy server jar at localJarPath to skip the runtime fetch.","Whitelist the scrcpy release URL through corporate proxies/firewalls.","Set HTTPS_PROXY/HTTP_PROXY so the fetch routes correctly in restricted networks."],"tags":["scrcpy","network","download","android","proxy"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}